/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ANDROID_SERVERS_CAMERA_CAMERA3_PREVIEWFRAMESPACER_H
#define ANDROID_SERVERS_CAMERA_CAMERA3_PREVIEWFRAMESPACER_H

#include <queue>

#include <gui/Surface.h>
#include <utils/Condition.h>
#include <utils/Mutex.h>
#include <utils/Thread.h>
#include <utils/Timers.h>

namespace android {

namespace camera3 {

class Camera3OutputStream;

/***
 * Preview stream spacer for better frame spacing
 *
 * The ideal viewfinder user experience is that frames are presented to the
 * user in the same cadence as outputed by the camera sensor. However, the
 * processing latency between frames could vary, due to factors such
 * as CPU load, differences in request settings, etc. This frame processing
 * latency results in variation in presentation of frames to the user.
 *
 * The PreviewFrameSpacer improves the viewfinder user experience by:
 * - Cache the frame buffers if the intervals between queueBuffer is shorter
 *   than the camera readout intervals.
 * - Queue frame buffers in the same cadence as the camera readout time.
 * - Maintain at most 1 queue-able buffer. If the 2nd preview buffer becomes
 *   available, queue the oldest cached buffer to the buffer queue.
 */
class PreviewFrameSpacer : public Thread {
  public:
    explicit PreviewFrameSpacer(wp<Camera3OutputStream> parent, sp<Surface> consumer);
    virtual ~PreviewFrameSpacer();

    // Queue preview buffer locally
    status_t queuePreviewBuffer(nsecs_t timestamp, nsecs_t readoutTimestamp,
            int32_t transform, ANativeWindowBuffer* anwBuffer, int releaseFence);

    bool threadLoop() override;
    void requestExit() override;
    status_t run(const char* name, int32_t priority = PRIORITY_DEFAULT, size_t stack = 0) override;

  private:
    // structure holding cached preview buffer info
    struct BufferHolder {
        nsecs_t timestamp;
        nsecs_t readoutTimestamp;
        int32_t transform;
        sp<ANativeWindowBuffer> anwBuffer;
        int releaseFence;

        BufferHolder(nsecs_t t, nsecs_t readoutT, int32_t tr, ANativeWindowBuffer* anwb, int rf) :
                timestamp(t), readoutTimestamp(readoutT), transform(tr), anwBuffer(anwb),
                releaseFence(rf) {}
    };

    void queueBufferToClientLocked(const BufferHolder& bufferHolder, nsecs_t currentTime);

    wp<Camera3OutputStream> mParent;
    sp<ANativeWindow> mConsumer;
    mutable Mutex mLock;
    Condition mBufferCond;

    std::queue<BufferHolder> mPendingBuffers;
    nsecs_t mLastCameraReadoutTime = 0;
    nsecs_t mLastCameraPresentTime = 0;
    static constexpr nsecs_t kWaitDuration = 5000000LL; // 50ms
    static constexpr nsecs_t kFrameIntervalThreshold = 80000000LL; // 80ms
    static constexpr nsecs_t kMaxFrameWaitTime = 10000000LL; // 10ms
    static constexpr nsecs_t kFrameAdjustThreshold = 2000000LL; // 2ms
};

}; //namespace camera3
}; //namespace android

#endif
