【Android FrameWork】第三十三天:Camera视频流写入SurfaceView的机制

Camera视频流写入SurfaceView的机制

应用层通过SurfaceView实现Camera预览时,仅需几行API调用即可看到视频流,但底层是Android系统多模块协同的复杂过程------从Camera硬件采集帧数据,到缓冲区流转,再到屏幕合成渲染,核心依赖BufferQueue的"生产者-消费者"模型。

本文将从系统源码视角,完整拆解"视频流写入Surface并绘制到界面"的全链路,并附上AOSP(Android开源项目)中的核心系统代码,揭示底层运行逻辑。

核心系统组件

在分析流程前,需先明确支撑视频流流转的核心系统组件,它们构成了"Camera→Surface→屏幕"的基础骨架:

组件 所属模块 核心角色
Camera Service frameworks/native/services/camera 系统级Camera管理服务,承接应用层Camera请求,桥接应用与Camera HAL
Camera HAL hardware/libhardware/modules/camera 硬件抽象层,直接与Camera驱动交互,负责帧数据采集,是BufferQueue的"生产者"
ANativeWindow frameworks/native/libs/gui 应用层Surface的系统层映射,封装BufferQueue生产者接口
BufferQueue frameworks/native/libs/gui 缓冲区队列管理器,协调生产者(Camera HAL)与消费者(SurfaceFlinger)的异步数据传递
SurfaceFlinger frameworks/native/services/surfaceflinger 系统合成服务,作为BufferQueue消费者,将视频流与其他UI层合成后渲染到屏幕
Gralloc hardware/libhardware/modules/gralloc 图形内存分配器,分配GPU/硬件可直接访问的缓冲区,避免CPU/GPU数据拷贝

核心设计思想

Android通过BufferQueue解耦"数据生产(Camera采集)"与"数据消费(屏幕渲染)":Camera HAL作为生产者将视频帧写入缓冲区,SurfaceFlinger作为消费者读取缓冲区并合成,二者通过ANativeWindow(应用Surface的系统层抽象)关联,全程基于硬件缓冲区复用,最大化渲染性能。

系统层视频流写入Surface的完整链路

整个过程可拆解为6个核心阶段,从应用层Surface创建到最终屏幕显示,每个阶段对应系统层的关键调用与数据流转:

阶段1:应用层Surface映射为系统层ANativeWindow

应用层SurfaceView创建后,通过SurfaceHolder.getSurface()获取的Surface是Java层抽象,系统层需将其转换为ANativeWindow(原生窗口),才能让Camera HAL操作缓冲区。

系统代码片段1:ANativeWindow_fromSurface实现(Surface→ANativeWindow)
cpp 复制代码
// 路径:frameworks/native/libs/gui/NativeWindow.cpp
#include <gui/Surface.h>
#include <jni.h>
#include <android/log.h>

#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, "NativeWindow", __VA_ARGS__)

ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surfaceObj) {
    if (surfaceObj == nullptr) {
        ALOGE("ANativeWindow_fromSurface: surfaceObj is null");
        return nullptr;
    }

    // 1. 通过JNI获取Java层Surface对应的原生Surface对象
    // android_view_Surface_getNativeSurface是系统JNI方法,返回Surface*
    sp<Surface> surface = android::android_view_Surface_getNativeSurface(env, surfaceObj);
    if (surface == nullptr) {
        ALOGE("ANativeWindow_fromSurface: native Surface is null");
        return nullptr;
    }

    // 2. Surface继承自ANativeWindow,直接强转
    ANativeWindow* window = static_cast<ANativeWindow*>(surface.get());
    // 3. 增加引用计数,防止Surface被提前释放
    ANativeWindow_acquire(window);
    return window;
}

核心逻辑

Java层Surface的原生实现是frameworks/native/libs/gui/Surface.cpp,其内部持有BufferQueue的生产者接口(IGraphicBufferProducer)。ANativeWindow本质是对该接口的封装,为后续Camera HAL写入缓冲区提供入口。

阶段2:Camera Service绑定ANativeWindow到Camera HAL

应用层调用Camera.setPreviewDisplay(Surface)(或C++层mCamera->setPreviewDisplay(ANativeWindow))后,最终通过Binder通信触发CameraServiceANativeWindow绑定到Camera HAL,让HAL知道"往哪个缓冲区写数据"。

系统代码片段2:CameraService绑定预览窗口
cpp 复制代码
// 路径:frameworks/native/services/camera/libcameraservice/CameraService.cpp
#include <camera/CameraService.h>
#include <gui/ANativeWindow.h>

namespace android {

status_t CameraService::Client::setPreviewWindow(const sp<ANativeWindow>& window) {
    ALOGD("CameraService::Client::setPreviewWindow: window=%p", window.get());
    Mutex::Autolock _l(mLock);

    // 1. 校验Camera状态:必须处于预览就绪状态
    if (mState != CAMERA_STATE_PREVIEW) {
        ALOGE("setPreviewWindow: invalid state %d (expected PREVIEW)", mState);
        return INVALID_OPERATION;
    }

    // 2. 释放旧窗口资源
    if (mPreviewWindow != nullptr) {
        mPreviewWindow->release();
        mPreviewWindow = nullptr;
    }

    // 3. 绑定新ANativeWindow,并传递给Camera HAL
    if (window != nullptr) {
        mPreviewWindow = window;
        mPreviewWindow->acquire();

        // 核心:将ANativeWindow传递给Camera HAL,HAL成为BufferQueue生产者
        status_t res = mHardware->setPreviewWindow(mPreviewWindow);
        if (res != NO_ERROR) {
            ALOGE("setPreviewWindow: HAL set failed, res=%d", res);
            mPreviewWindow->release();
            mPreviewWindow = nullptr;
            return res;
        }
    }

    return NO_ERROR;
}

} // namespace android

核心逻辑
CameraService作为系统服务,承接应用层的预览窗口绑定请求,将ANativeWindow(即Surface的系统层实例)转发给Camera HAL。此时,Camera HAL与BufferQueue的生产者接口完成绑定,具备了写入缓冲区的能力。

阶段3:Camera HAL采集视频帧(从硬件驱动到内存)

应用层调用Camera.startPreview()后,最终触发Camera HAL启动预览线程,从Camera硬件驱动读取原始YUV帧数据(如YUV420SP),为写入缓冲区做准备。

系统代码片段3:Camera HAL预览线程与帧采集
cpp 复制代码
// 路径:hardware/libhardware/modules/camera/3_0/Camera.cpp(Camera HAL 3.0示例)
#include <hardware/camera3.h>
#include <utils/Thread.h>

namespace android {

class Camera : public RefBase {
public:
    status_t startPreview();
    void stopPreview();

private:
    // 预览线程:循环从Camera驱动读取帧数据
    class PreviewThread : public Thread {
    public:
        PreviewThread(sp<Camera> camera) : mCamera(camera) {}
        virtual bool threadLoop() override;
    private:
        wp<Camera> mCamera;
    };

    sp<camera3_device_t> mDevice; // Camera驱动设备指针
    sp<PreviewThread> mPreviewThread;
    bool mIsPreviewing;
    sp<ANativeWindow> mPreviewWindow; // 绑定的预览窗口
};

// 启动预览:配置驱动并启动预览线程
status_t Camera::startPreview() {
    Mutex::Autolock _l(mLock);
    if (mIsPreviewing) return NO_ERROR;

    // 1. 配置Camera驱动的预览参数(分辨率、格式、帧率)
    camera_stream_configuration_t config = {
        .stream_count = 1,
        .streams = &mPreviewStream, // 预览流:1920x1080 YUV420SP
        .operation_mode = CAMERA3_TEMPLATE_PREVIEW
    };
    int res = mDevice->ops->configure_streams(mDevice, &config);
    if (res != OK) {
        ALOGE("startPreview: configure streams failed, res=%d", res);
        return res;
    }

    // 2. 启动预览线程
    mPreviewThread = new PreviewThread(this);
    if (mPreviewThread->run("CameraPreviewThread", PRIORITY_URGENT_DISPLAY) != OK) {
        ALOGE("startPreview: start thread failed");
        mPreviewThread.clear();
        return UNKNOWN_ERROR;
    }

    mIsPreviewing = true;
    return NO_ERROR;
}

// 预览线程主逻辑:循环读取驱动帧数据并写入缓冲区
bool Camera::PreviewThread::threadLoop() {
    sp<Camera> camera = mCamera.promote();
    if (camera == nullptr || !camera->mIsPreviewing) return false;

    camera3_capture_result_t* result;
    // 1. 从Camera驱动阻塞等待一帧数据
    int res = camera->mDevice->ops->wait_for_frame(camera->mDevice, &result);
    if (res != OK) {
        ALOGE("PreviewThread: wait_for_frame failed, res=%d", res);
        return true; // 继续循环,不退出线程
    }

    // 2. 将驱动返回的YUV帧写入ANativeWindow(核心:写入Surface缓冲区)
    camera->writeFrameToWindow(result->output_buffers[0]);

    // 3. 释放驱动帧资源
    camera->mDevice->ops->return_frame(camera->mDevice, result);
    return true;
}

} // namespace android

核心逻辑
Camera HAL通过独立线程(PreviewThread)与硬件驱动交互,以阻塞方式读取原始YUV帧数据。读取完成后,调用writeFrameToWindow将数据写入ANativeWindow关联的缓冲区------这是"视频流写入Surface"的核心操作。

阶段4:Camera HAL写入BufferQueue(生产者核心操作)

Camera HAL通过ANativeWindow的核心接口(dequeueBuffer/lockBuffer/queueBuffer)完成缓冲区的申请、写入、提交,最终将帧数据放入BufferQueue的"已排队"队列,等待消费者读取。

系统代码片段4:ANativeWindow缓冲区写入实现
cpp 复制代码
// 路径:frameworks/native/libs/gui/Surface.cpp(ANativeWindow的核心实现)
#include <gui/Surface.h>
#include <gui/BufferQueue.h>

namespace android {

// 申请空闲缓冲区(从BufferQueue获取)
status_t Surface::dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd) {
    Mutex::Autolock _l(mLock);
    sp<IGraphicBufferProducer> producer = mGraphicBufferProducer;
    if (producer == nullptr) return NO_INIT;

    // 1. 调用BufferQueueProducer的dequeueBuffer,获取空闲缓冲区
    int slot;
    sp<Fence> fence;
    status_t res = producer->dequeueBuffer(&slot, &fence, mWidth, mHeight, mFormat, 0);
    if (res != OK) {
        ALOGE("dequeueBuffer failed, res=%d", res);
        return res;
    }

    // 2. 将缓冲区转换为ANativeWindowBuffer返回给HAL
    *buffer = mSlots[slot].mBuffer.get();
    *fenceFd = fence->dup();

    mLastDequeuedSlot = slot;
    return OK;
}

// 提交写入完成的缓冲区(放入BufferQueue已排队队列)
status_t Surface::queueBuffer(ANativeWindowBuffer* buffer, int fenceFd) {
    Mutex::Autolock _l(mLock);
    sp<IGraphicBufferProducer> producer = mGraphicBufferProducer;
    if (producer == nullptr) return NO_INIT;

    // 1. 找到缓冲区对应的slot
    int slot = getSlotFromBufferLocked(buffer);
    if (slot < 0) return BAD_VALUE;

    // 2. 调用BufferQueueProducer的queueBuffer,标记缓冲区为"已排队"
    sp<Fence> fence = new Fence(fenceFd);
    IGraphicBufferProducer::QueueBufferInput input(0, mTimestamp,
            NATIVE_WINDOW_SCALING_MODE_FREEZE, Rect(mWidth, mHeight),
            NATIVE_WINDOW_TRANSFORM_IDENTITY, false, fence);
    IGraphicBufferProducer::QueueBufferOutput output;
    status_t res = producer->queueBuffer(slot, input, &output);

    // 3. 更新缓冲区状态
    if (res == OK) {
        mSlots[slot].mBufferState = BufferSlot::QUEUED;
    }

    return res;
}

} // namespace android

// Camera HAL调用的写入逻辑(伪代码,简化核心流程)
status_t Camera::writeFrameToWindow(const camera3_stream_buffer_t& buffer) {
    ANativeWindowBuffer* nativeBuffer = nullptr;
    int fenceFd = -1;

    // 步骤1:申请空闲缓冲区
    status_t res = mPreviewWindow->dequeueBuffer(mPreviewWindow, &nativeBuffer, &fenceFd);
    if (res != OK) return res;

    // 步骤2:锁定缓冲区,获取可写入的内存地址
    void* dstPtr = nullptr;
    res = mPreviewWindow->lockBuffer(mPreviewWindow, nativeBuffer, &dstPtr);
    if (res != OK) {
        mPreviewWindow->cancelBuffer(mPreviewWindow, nativeBuffer, fenceFd);
        return res;
    }

    // 步骤3:将YUV帧数据拷贝到缓冲区(硬件DMA拷贝,无CPU参与)
    memcpy(dstPtr, buffer.buffer, buffer.size);

    // 步骤4:提交缓冲区到BufferQueue
    res = mPreviewWindow->queueBuffer(mPreviewWindow, nativeBuffer, fenceFd);
    if (res != OK) {
        ALOGE("queueBuffer failed, res=%d", res);
        return res;
    }

    return OK;
}

核心逻辑

  1. dequeueBuffer:从BufferQueue申请一个空闲缓冲区(状态:FREE→DEQUEUED);
  2. lockBuffer:锁定缓冲区,获取内存地址,确保CPU/GPU可安全写入;
  3. 数据拷贝:将Camera驱动的YUV数据写入缓冲区(高端设备通过DMA直接拷贝,无CPU开销);
  4. queueBuffer:提交缓冲区,状态变为QUEUED,BufferQueue通知消费者(SurfaceFlinger)有新帧可读取。

阶段5:SurfaceFlinger读取缓冲区并合成

SurfaceFlinger作为系统合成服务,定期扫描所有活跃的BufferQueue,读取"已排队"的视频帧缓冲区,将其转换为GPU纹理后,与其他UI层(如按钮、标题栏)合成。

系统代码片段5:SurfaceFlinger读取并合成缓冲区
cpp 复制代码
// 路径:frameworks/native/services/surfaceflinger/Layer.cpp(Surface对应Layer)
#include <surfaceflinger/Layer.h>
#include <gui/BufferQueueConsumer.h>

namespace android {

// 从BufferQueue读取新帧并更新纹理
status_t Layer::updateTexImage() {
    Mutex::Autolock _l(mLock);
    sp<BufferQueueConsumer> consumer = mConsumer;
    if (consumer == nullptr) return NO_INIT;

    // 1. 从BufferQueueConsumer获取"已排队"的缓冲区(状态:QUEUED→ACQUIRED)
    BufferQueue::BufferItem item;
    status_t res = consumer->acquireBuffer(&item, 0);
    if (res != OK) {
        if (res != NO_BUFFER_AVAILABLE) {
            ALOGE("updateTexImage: acquireBuffer failed, res=%d", res);
        }
        return res;
    }

    // 2. 将缓冲区数据上传到GPU纹理(为合成做准备)
    res = mTexture->upload(item.mGraphicBuffer);
    if (res != OK) {
        consumer->releaseBuffer(item.mSlot, item.mFrameNumber);
        return res;
    }

    // 3. 释放旧缓冲区(状态:ACQUIRED→FREE,供Camera HAL复用)
    if (mLastAcquiredSlot != BufferQueue::INVALID_BUFFER_SLOT) {
        consumer->releaseBuffer(mLastAcquiredSlot, mLastAcquiredFrame);
    }

    mLastAcquiredSlot = item.mSlot;
    return OK;
}

// 路径:frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
void SurfaceFlinger::compositeDisplay(const sp<DisplayDevice>& display) {
    // 1. 初始化GPU渲染上下文
    GLRenderer& renderer = display->getRenderer();
    renderer.beginFrame(display->getDisplayRect());

    // 2. 按Z轴顺序遍历所有Layer(Camera预览Layer通常在底层)
    const LayerVector& layers = display->getVisibleLayersSortedByZ();
    for (const auto& layer : layers) {
        if (!layer->isVisible()) continue;

        // 3. 绘制Layer到合成缓冲区(Camera视频帧纹理)
        renderer.drawLayer(layer, layer->getTexture(), layer->getMatrix());
    }

    // 4. 完成合成,提交到屏幕帧缓冲区
    renderer.endFrame();
}

} // namespace android

核心逻辑

  • 每个应用SurfaceSurfaceFlinger中对应一个LayerLayer持有BufferQueueConsumer接口,负责读取缓冲区;
  • updateTexImage:从BufferQueue获取新帧,转换为GPU纹理,同时释放旧缓冲区供生产者复用;
  • compositeDisplay:将所有Layer的纹理按Z轴顺序合成(Camera预览Layer通常在最底层),生成最终的屏幕帧。

阶段6:硬件合成与屏幕显示

SurfaceFlinger完成合成后,通过HWComposer(硬件合成器)将最终帧数据写入屏幕的帧缓冲区(FrameBuffer),屏幕驱动以60Hz/90Hz的频率读取帧缓冲区数据,最终显示到物理屏幕上。

系统代码片段6:HWComposer硬件合成提交
cpp 复制代码
// 路径:frameworks/native/services/surfaceflinger/HWComposer.cpp
#include <surfaceflinger/HWComposer.h>

namespace android {

status_t HWComposer::commit(int displayId) {
    auto& display = mDisplays[displayId];
    if (!display.isValid()) return BAD_VALUE;

    // 1. 将合成后的帧数据提交到硬件合成器
    int res = display.device->commit(display.device);
    if (res != 0) {
        ALOGE("HWComposer commit failed, res=%d", res);
        return res;
    }

    // 2. 等待VSync信号(屏幕垂直同步),确保帧显示不撕裂
    display.vsync.wait();
    return OK;
}

} // namespace android

核心逻辑
HWComposer通过硬件加速完成最终的帧合成,避免CPU/GPU的额外开销;同时等待VSync信号,确保每帧数据在屏幕刷新的间隙写入,防止画面撕裂。

完整链路总结与常见问题根因

1 全链路梳理

复制代码
应用层:SurfaceView创建Surface → JNI转换为ANativeWindow → 绑定到Camera
↓
系统层:CameraService将ANativeWindow转发给Camera HAL
↓
HAL层:启动预览线程 → 从驱动读取YUV帧 → dequeueBuffer申请缓冲区 → 写入数据 → queueBuffer提交到BufferQueue
↓
SurfaceFlinger:acquireBuffer读取缓冲区 → 转换为GPU纹理 → 与UI层合成 → HWComposer提交到屏幕帧缓冲区
↓
硬件层:屏幕驱动读取帧缓冲区 → 显示到物理屏幕

2 常见问题的系统层根因

应用层现象 系统层根因
预览卡顿 BufferQueue缓冲区数量不足;Camera HAL采集帧率低于屏幕刷新率;SurfaceFlinger合成耗时过长
画面拉伸/变形 Camera预览尺寸与Surface尺寸宽高比不匹配;BufferQueue缓冲区缩放模式配置错误
预览花屏/绿屏 YUV帧格式与Surface配置的格式不兼容;缓冲区数据拷贝过程中内存越界/地址错误
预览黑屏 ANativeWindow引用计数错误导致Surface被释放;Camera HAL未正确提交缓冲区到BufferQueue

总结

Android系统将Camera视频流写入Surface并绘制到界面的核心,是基于BufferQueue的"生产者-消费者"模型:Camera HAL作为生产者完成帧采集与缓冲区写入,SurfaceFlinger作为消费者完成缓冲区读取与合成,ANativeWindow则是连接应用层与系统层的桥梁。

理解这一底层机制,不仅能解释应用层预览的各种异常现象,更能指导高性能预览的优化方向------比如通过减少缓冲区拷贝、匹配Camera预览尺寸与Surface尺寸、启用HWComposer硬件合成等方式,提升预览流畅度与画质。

相关推荐
csj503 小时前
安卓基础之《(4)—Activity组件(2)》
android
_李小白4 小时前
【Android GLSurfaceView源码学习】第二天:GLSurfaceView深度分析
android·学习
元气满满-樱5 小时前
LNMP架构学习
android·学习·架构
allk556 小时前
Android 渲染性能优化实战总结:从监控体系到架构落地
android·性能优化·架构
思成不止于此6 小时前
C++红黑树封装map/set核心揭秘
android
走在路上的菜鸟6 小时前
Android学Dart学习笔记第十七节 类-成员方法
android·笔记·学习·flutter
歪楼小能手6 小时前
Android16底部导航栏添加音量加减虚拟按键
android·java·平板
又是努力搬砖的一年6 小时前
elasticsearch修改字段类型
android·大数据·elasticsearch
走在路上的菜鸟6 小时前
Android学Dart学习笔记第十八节 类-继承
android·笔记·学习·flutter