1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.camera.one.v2.errorhandling;
18
19import android.annotation.TargetApi;
20import android.hardware.camera2.CaptureResult;
21import android.hardware.camera2.TotalCaptureResult;
22import android.os.Build.VERSION_CODES;
23
24import com.android.camera.debug.Log.Tag;
25import com.android.camera.debug.Logger;
26import com.android.camera.one.v2.core.ResponseListener;
27import com.android.camera.stats.UsageStatistics;
28
29import javax.annotation.ParametersAreNonnullByDefault;
30
31/**
32 * Detect jank in the preview by detecting large percentage increases in the time
33 * delta between the sensor timestamps retrieved from the camera.
34 */
35@ParametersAreNonnullByDefault
36@TargetApi(VERSION_CODES.LOLLIPOP)
37public final class FramerateJankDetector extends ResponseListener {
38    private static final double FRACTIONAL_CHANGE_STATS_THRESHOLD = .5;
39    private static final double FRACTIONAL_CHANGE_LOG_THRESHOLD = 1.5;
40
41    private final Logger mLog;
42    private final UsageStatistics mUsageStatistics;
43
44    private long mLastFrameTimestamp = -1;
45    private double mLastDeltaMillis = 0.0;
46
47    /**
48     * @param logFactory the logger to use when over the logs threshold.
49     * @param usageStatistics the usage statistics to report to when over the
50     *                        statistics reporting threshold.
51     */
52    public FramerateJankDetector(Logger.Factory logFactory, UsageStatistics usageStatistics) {
53        mLog = logFactory.create(new Tag("FrameJank"));
54        mUsageStatistics = usageStatistics;
55        mUsageStatistics.jankDetectionEnabled();
56    }
57
58    @Override
59    public void onCompleted(TotalCaptureResult result) {
60        long timestamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
61        if (mLastFrameTimestamp >= 0) {
62            double deltaMillis = (timestamp - mLastFrameTimestamp) / 1000000.0;
63
64            if (mLastDeltaMillis > 0) {
65                double fractionalChange = (deltaMillis - mLastDeltaMillis) / mLastDeltaMillis;
66                if (fractionalChange >= FRACTIONAL_CHANGE_STATS_THRESHOLD) {
67                    mUsageStatistics.cameraFrameDrop(deltaMillis, mLastDeltaMillis);
68                }
69
70                if (fractionalChange >= FRACTIONAL_CHANGE_LOG_THRESHOLD) {
71                    mLog.v("JANK! Time between frames (" + deltaMillis + "ms) increased by " +
72                          (fractionalChange * 100) + "% over the last frame delta (" +
73                          mLastDeltaMillis + "ms)");
74                }
75            }
76            mLastDeltaMillis = deltaMillis;
77        }
78
79        mLastFrameTimestamp = timestamp;
80    }
81}
82