【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硬件合成等方式,提升预览流畅度与画质。

相关推荐
千里马学框架20 小时前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台20 小时前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone20 小时前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc21 小时前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo1 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077001 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼1 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone1 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen1 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone1 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui