2. 预览流性能调优

任务说明

本案例针对Android相机HAL层预览流延迟问题,通过两项核心调整——缓冲区管理与线程优先级——实现端到端延迟的降低。你将基于一个最小可复现的HAL预览流实现,完成以下目标:

  1. 实现预览请求的缓冲区提前排队机制,减少帧等待时间;
  2. 将预览流处理线程设置为实时优先级,降低调度延迟;
  3. 通过时间戳测量验证端到端延迟的改善。

核心实现

// preview_latency_optimizer.h
#ifndef PREVIEW_LATENCY_OPTIMIZER_H
#define PREVIEW_LATENCY_OPTIMIZER_H

#include <system/camera_metadata.h>
#include <utils/Mutex.h>
#include <utils/Thread.h>
#include <list>
#include <chrono>

namespace android {

// 预览流延迟优化器
class PreviewLatencyOptimizer : public Thread {
public:
    PreviewLatencyOptimizer();
    ~PreviewLatencyOptimizer();

    // 初始化缓冲区池和线程参数
    status_t initialize(size_t bufferCount, int priority);

    // 提交预览请求(由HAL调用)
    status_t submitPreviewRequest(buffer_handle_t buffer, nsecs_t timestamp);

    // 获取当前平均端到端延迟(纳秒)
    nsecs_t getAverageLatency() const;

    // 线程循环
    virtual bool threadLoop() override;

private:
    // 缓冲区提前排队:维护待处理请求队列
    struct PendingRequest {
        buffer_handle_t buffer;
        nsecs_t requestTime;
    };

    std::list<PendingRequest> mPendingQueue;
    Mutex mQueueLock;
    Condition mQueueCond;

    // 延迟统计
    mutable Mutex mLatencyLock;
    nsecs_t mTotalLatencyNs;
    size_t mFrameCount;

    // 配置参数
    size_t mBufferCount;
    int mThreadPriority;

    // 处理单个预览请求
    void processRequest(const PendingRequest& req);
};

} // namespace android

#endif // PREVIEW_LATENCY_OPTIMIZER_H
// preview_latency_optimizer.cpp
#include "preview_latency_optimizer.h"
#include <utils/Log.h>
#include <cutils/sched_policy.h>
#include <sys/resource.h>

namespace android {

PreviewLatencyOptimizer::PreviewLatencyOptimizer()
    : Thread(/*canCallJava*/ false),
      mTotalLatencyNs(0),
      mFrameCount(0),
      mBufferCount(0),
      mThreadPriority(0) {}

PreviewLatencyOptimizer::~PreviewLatencyOptimizer() {
    requestExit();
}

status_t PreviewLatencyOptimizer::initialize(size_t bufferCount, int priority) {
    mBufferCount = bufferCount;
    mThreadPriority = priority;

    // 启动处理线程
    status_t err = run("preview_latency_opt", priority);
    if (err != OK) {
        ALOGE("Failed to start preview latency optimizer thread: %d", err);
        return err;
    }

    // 设置线程调度策略为实时(需要root或相应权限)
    if (priority > 0) {
        set_sched_policy(getTid(), SP_FOREGROUND);
        setpriority(PRIO_PROCESS, getTid(), priority);
    }

    return OK;
}

status_t PreviewLatencyOptimizer::submitPreviewRequest(buffer_handle_t buffer, nsecs_t timestamp) {
    Mutex::Autolock lock(mQueueLock);

    // 缓冲区管理:当队列满时丢弃最旧请求,保证延迟可控
    if (mPendingQueue.size() >= mBufferCount) {
        mPendingQueue.pop_front();
        ALOGV("Preview queue full, dropping oldest request");
    }

    PendingRequest req;
    req.buffer = buffer;
    req.requestTime = timestamp;
    mPendingQueue.push_back(req);
    mQueueCond.signal();
    return OK;
}

nsecs_t PreviewLatencyOptimizer::getAverageLatency() const {
    Mutex::Autolock lock(mLatencyLock);
    if (mFrameCount == 0) return 0;
    return mTotalLatencyNs / mFrameCount;
}

bool PreviewLatencyOptimizer::threadLoop() {
    Mutex::Autolock lock(mQueueLock);
    while (mPendingQueue.empty()) {
        mQueueCond.wait(mQueueLock);
    }

    PendingRequest req = mPendingQueue.front();
    mPendingQueue.pop_front();
    lock.unlock();

    processRequest(req);
    return true;
}

void PreviewLatencyOptimizer::processRequest(const PendingRequest& req) {
    // 模拟帧处理完成时间点
    nsecs_t completionTime = systemTime(SYSTEM_TIME_MONOTONIC);
    nsecs_t latency = completionTime - req.requestTime;

    // 更新延迟统计
    Mutex::Autolock lock(mLatencyLock);
    mTotalLatencyNs += latency;
    mFrameCount++;
}

} // namespace android
// 测试驱动:验证延迟优化效果
#include "preview_latency_optimizer.h"
#include <utils/SystemClock.h>
#include <cstdio>

using namespace android;

int main() {
    PreviewLatencyOptimizer optimizer;

    // 初始化:4个缓冲区,线程优先级-8(实时范围-20~19)
    if (optimizer.initialize(4, -8) != OK) {
        printf("Failed to initialize optimizer\n");
        return -1;
    }

    // 模拟提交20帧预览请求,间隔10ms
    const int kFrameCount = 20;
    const nsecs_t kFrameIntervalMs = 10;

    for (int i = 0; i < kFrameCount; i++) {
        nsecs_t requestTime = systemTime(SYSTEM_TIME_MONOTONIC);
        optimizer.submitPreviewRequest(nullptr, requestTime);
        usleep(kFrameIntervalMs * 1000);
    }

    // 等待所有请求处理完成
    usleep(500 * 1000);

    // 输出平均延迟
    nsecs_t avgLatency = optimizer.getAverageLatency();
    printf("Average preview latency: %.2f ms\n", avgLatency / 1000000.0);

    return 0;
}

编译与运行

# 编译(需Android源码环境或NDK)
c++ -std=c++17 -I./include \
    preview_latency_optimizer.cpp test_preview_latency.cpp \
    -lutils -lcutils -lpthread \
    -o preview_latency_test

# 运行(需要root权限以设置实时优先级)
adb root
adb push preview_latency_test /data/local/tmp/
adb shell /data/local/tmp/preview_latency_test

验证方法与判定标准

运行测试程序后,观察输出结果。判定标准如下:

  • 功能正确性:程序正常退出,无崩溃或死锁;
  • 延迟指标:平均延迟应小于帧间隔(10ms),即输出值应小于10.00ms。若大于该值,说明缓冲区排队或线程优先级设置未生效;
  • 缓冲区管理验证:在代码中增加日志输出(ALOGV),观察当提交速度超过处理速度时,队列是否按预期丢弃最旧请求,且不出现内存泄漏或野指针;
  • 线程优先级验证:在运行期间通过cat /proc/<pid>/task/<tid>/stat查看线程优先级,确认其nice值已设置为-8。

若以上条件均满足,则说明通过缓冲区提前排队和实时线程优先级调整,预览流端到端延迟已得到有效控制。实际部署到相机HAL时,可将submitPreviewRequest接入HAL的processCaptureRequest入口,并将processRequest替换为真实的帧处理逻辑。