Files
DisplayFlow/docs/VIDEO_FLOW_WINDOWS_TO_ANDROID.md
huanglinhuan 30f45f8397 更新文档
2025-12-18 21:30:08 +08:00

19 KiB
Raw Blame History

Windows Host → Android Client 视频流向详解

本文档详细说明 Windows 设备作为 HostAndroid 设备作为 Client 时的完整视频流向。

完整数据流图

┌─────────────────────────────────────────────────────────────────┐
│ Windows Host 设备(发送端)                                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│ [1] 屏幕捕获层                                                   │
│     Desktop Duplication API / GDI BitBlt                        │
│     ↓ 输出D3D11 纹理GPU 纹理,零拷贝)                       │
│                                                                   │
│ [2] 编码层                                                       │
│     Media Foundation H.264 硬件编码器 / D3D11 Video Encoder     │
│     ↓ 输出H.264 NAL 单元(压缩后的视频数据)                   │
│                                                                   │
│ [3] 协议封装层                                                   │
│     FlatBuffers 序列化                                           │
│     - 帧头信息(时间戳、分辨率、帧类型 I/P                     │
│     - 编码数据NAL 单元)                                       │
│     ↓ 输出:序列化的协议消息(二进制数据)                       │
│                                                                   │
│ [4] 网络传输层                                                   │
│     UDP Socket (USB RNDIS / Wi-Fi / 以太网)                     │
│     - 分包处理MTU 限制,通常 1500 字节)                       │
│     - 重传机制(关键帧 I-frame                                 │
│     ↓ 输出UDP 数据包                                           │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘
                          ↓ 网络传输
                          ↓ 延迟1-5ms (USB RNDIS) / 5-20ms (Wi-Fi)
┌─────────────────────────────────────────────────────────────────┐
│ Android Client 设备(接收端)                                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│ [5] 网络接收层                                                   │
│     UDP Socket 接收                                              │
│     - 包重组(处理分包)                                         │
│     - 顺序保证(处理乱序)                                       │
│     ↓ 输出:完整的协议消息                                       │
│                                                                   │
│ [6] 协议解析层                                                   │
│     FlatBuffers 反序列化                                         │
│     - 提取帧头信息                                               │
│     - 提取编码数据NAL 单元)                                   │
│     ↓ 输出H.264 NAL 单元                                       │
│                                                                   │
│ [7] 解码层                                                       │
│     AMediaCodec (H.264 硬件解码器)                               │
│     ↓ 输出GPU 纹理AHardwareBuffer零拷贝                  │
│                                                                   │
│ [8] 渲染层                                                       │
│     OpenGL ES / ANativeWindow                                   │
│     - 纹理复制到渲染目标                                         │
│     - Surface 更新                                               │
│     ↓ 输出:渲染后的画面                                         │
│                                                                   │
│ [9] 显示输出                                                     │
│     Android Surface / SurfaceView                               │
│     - 直接显示在 Android 屏幕上                                  │
│     - 支持全屏或窗口模式                                         │
│     ↓ 最终显示在 Android 设备屏幕上                              │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

各阶段详细说明

Windows Host 端

[1] 屏幕捕获层

技术方案Desktop Duplication API推荐

// platforms/windows/src/capture/screen_capture.cpp
#include <dxgi1_2.h>
#include <d3d11.h>

class ScreenCapture {
public:
    bool Initialize() {
        // 创建 D3D11 设备
        D3D11CreateDevice(
            nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr,
            0, nullptr, 0, D3D11_SDK_VERSION,
            &device_, nullptr, &context_
        );
        
        // 获取 DXGI 输出
        IDXGIOutput* output = nullptr;
        // ... 获取主显示器输出
        
        // 创建 Desktop Duplication
        IDXGIOutput1* output1 = nullptr;
        output->QueryInterface(__uuidof(IDXGIOutput1), (void**)&output1);
        output1->DuplicateOutput(device_, &duplication_);
        
        return true;
    }
    
    bool CaptureFrame(ID3D11Texture2D** texture) {
        DXGI_OUTDUPL_FRAME_INFO frameInfo;
        IDXGIResource* resource = nullptr;
        
        // 获取桌面帧
        HRESULT hr = duplication_->AcquireNextFrame(
            0, &frameInfo, &resource
        );
        
        if (SUCCEEDED(hr)) {
            // 获取纹理
            resource->QueryInterface(__uuidof(ID3D11Texture2D), (void**)texture);
            duplication_->ReleaseFrame();
            return true;
        }
        
        return false;
    }
    
private:
    ID3D11Device* device_ = nullptr;
    ID3D11DeviceContext* context_ = nullptr;
    IDXGIOutputDuplication* duplication_ = nullptr;
};

特点:

  • 使用 Desktop Duplication APIWindows 8+
  • 直接获取 GPU 纹理,零拷贝
  • 支持多显示器
  • 性能最优

备选方案GDI BitBlt

  • 适用于较旧的 Windows 版本
  • 需要 CPU 拷贝,性能较低

[2] 编码层

技术方案Media Foundation H.264 硬件编码器

// platforms/windows/src/codec/windows_h264_encoder.cpp
#include <mfapi.h>
#include <mftransform.h>
#include <mferror.h>

class WindowsH264Encoder {
public:
    bool Initialize(int width, int height, int bitrate, int fps) {
        // 创建 Media Foundation 编码器
        IMFActivate** activates = nullptr;
        UINT32 count = 0;
        
        MFTEnum(
            MFT_CATEGORY_VIDEO_ENCODER,
            MFT_ENUM_FLAG_HARDWARE,
            &inputType, nullptr,
            &activates, &count
        );
        
        // 选择 H.264 编码器
        activates[0]->ActivateObject(IID_PPV_ARGS(&encoder_));
        
        // 配置编码器
        IMFMediaType* inputType = nullptr;
        MFCreateMediaType(&inputType);
        inputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
        inputType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_NV12);
        inputType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
        MFSetAttributeSize(inputType, MF_MT_FRAME_SIZE, width, height);
        MFSetAttributeRatio(inputType, MF_MT_FRAME_RATE, fps, 1);
        
        IMFMediaType* outputType = nullptr;
        MFCreateMediaType(&outputType);
        outputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
        outputType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264);
        outputType->SetUINT32(MF_MT_AVG_BITRATE, bitrate);
        outputType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
        
        encoder_->SetInputType(0, inputType, 0);
        encoder_->SetOutputType(0, outputType, 0);
        
        encoder_->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, 0);
        encoder_->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0);
        encoder_->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0);
        
        return true;
    }
    
    bool EncodeFrame(ID3D11Texture2D* inputTexture, std::vector<uint8_t>& output) {
        // 将 D3D11 纹理转换为 Media Foundation Sample
        IMFSample* sample = nullptr;
        // ... 转换逻辑
        
        // 编码
        encoder_->ProcessInput(0, sample, 0);
        
        // 获取编码输出
        MFT_OUTPUT_DATA_BUFFER outputBuffer = {};
        DWORD status = 0;
        encoder_->ProcessOutput(0, 1, &outputBuffer, &status);
        
        // 提取编码数据
        IMFMediaBuffer* buffer = nullptr;
        outputBuffer.pSample->ConvertToContiguousBuffer(&buffer);
        
        BYTE* data = nullptr;
        DWORD length = 0;
        buffer->Lock(&data, nullptr, &length);
        output.assign(data, data + length);
        buffer->Unlock();
        
        return true;
    }
    
private:
    IMFTransform* encoder_ = nullptr;
};

特点:

  • 使用 Media Foundation 硬件编码器
  • 支持 H.264 编码
  • 硬件加速CPU 占用低
  • 支持动态码率调整

备选方案D3D11 Video Encoder

  • 更底层的 API
  • 性能可能更好,但实现更复杂

[3] 协议封装层

// core/src/protocol/message_serializer.cpp
#include "displayflow/core/protocol/message_serializer.h"

class MessageSerializer {
public:
    ByteArray SerializeVideoFrame(const VideoFrame& frame) {
        // 使用 FlatBuffers 序列化
        flatbuffers::FlatBufferBuilder builder;
        
        auto frameData = builder.CreateVector(frame.data.data(), frame.data.size());
        auto message = CreateVideoFrameMessage(
            builder,
            frame.timestamp,
            frame.width,
            frame.height,
            frame.frameType,  // I-frame or P-frame
            frameData
        );
        
        builder.Finish(message);
        return ByteArray(builder.GetBufferPointer(), 
                        builder.GetBufferPointer() + builder.GetSize());
    }
};

[4] 网络传输层

// core/src/network/network_manager.cpp
class NetworkManager {
public:
    bool SendVideoFrame(const ByteArray& data) {
        // UDP Socket 发送
        // 处理分包MTU 限制)
        const size_t MTU = 1500;
        size_t offset = 0;
        
        while (offset < data.size()) {
            size_t chunkSize = std::min(MTU, data.size() - offset);
            sendto(socket_, 
                   data.data() + offset, chunkSize,
                   0, (sockaddr*)&clientAddr_, sizeof(clientAddr_));
            offset += chunkSize;
        }
        
        return true;
    }
};

Android Client 端

[5] 网络接收层

// core/src/network/network_manager.cpp
class NetworkManager {
public:
    bool ReceiveVideoFrame(ByteArray& data) {
        // UDP Socket 接收
        // 包重组和顺序保证
        char buffer[MTU];
        sockaddr_in fromAddr;
        socklen_t addrLen = sizeof(fromAddr);
        
        ssize_t received = recvfrom(socket_, buffer, MTU, 0,
                                   (sockaddr*)&fromAddr, &addrLen);
        
        if (received > 0) {
            // 处理分包重组
            // 保证顺序
            data.insert(data.end(), buffer, buffer + received);
            return true;
        }
        
        return false;
    }
};

[6] 协议解析层

// core/src/protocol/message_serializer.cpp
class MessageSerializer {
public:
    bool DeserializeVideoFrame(const ByteArray& data, VideoFrame& frame) {
        // FlatBuffers 反序列化
        auto message = GetVideoFrameMessage(data.data());
        
        frame.timestamp = message->timestamp();
        frame.width = message->width();
        frame.height = message->height();
        frame.frameType = message->frameType();
        
        auto frameData = message->data();
        frame.data.assign(frameData->begin(), frameData->end());
        
        return true;
    }
};

[7] 解码层

技术方案AMediaCodec 硬件解码器

// platforms/android/src/render/render_engine.cpp
#include <media/NdkMediaCodec.h>
#include <media/NdkMediaFormat.h>

class RenderEngine {
public:
    bool Initialize(int width, int height) {
        // 创建解码器
        const char* mimeType = "video/avc";
        decoder_ = AMediaCodec_createDecoderByType(mimeType);
        
        // 配置解码器
        AMediaFormat* format = AMediaFormat_new();
        AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, mimeType);
        AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_WIDTH, width);
        AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_HEIGHT, height);
        
        // 获取输出 Surface用于渲染
        ANativeWindow* surface = GetOutputSurface();
        
        AMediaCodec_configure(decoder_, format, surface, nullptr, 0);
        AMediaCodec_start(decoder_);
        
        return true;
    }
    
    bool DecodeFrame(const ByteArray& h264Data) {
        // 输入编码数据
        ssize_t inputBufferId = AMediaCodec_dequeueInputBuffer(decoder_, 10000);
        if (inputBufferId >= 0) {
            size_t inputSize = 0;
            uint8_t* inputBuffer = AMediaCodec_getInputBuffer(
                decoder_, inputBufferId, &inputSize);
            
            memcpy(inputBuffer, h264Data.data(), h264Data.size());
            
            AMediaCodec_queueInputBuffer(
                decoder_, inputBufferId, 0, h264Data.size(),
                0, 0);
        }
        
        // 获取解码输出
        AMediaCodecBufferInfo bufferInfo;
        ssize_t outputBufferId = AMediaCodec_dequeueOutputBuffer(
            decoder_, &bufferInfo, 10000);
        
        if (outputBufferId >= 0) {
            // 解码完成,直接渲染到 Surface零拷贝
            AMediaCodec_releaseOutputBuffer(decoder_, outputBufferId, true);
            return true;
        }
        
        return false;
    }
    
private:
    AMediaCodec* decoder_ = nullptr;
};

特点:

  • 使用 AMediaCodec 硬件解码器
  • 直接输出到 Surface零拷贝
  • 硬件加速,性能最优

[8] 渲染层

技术方案ANativeWindow + OpenGL ES

// platforms/android/src/render/render_engine.cpp
#include <android/native_window.h>
#include <EGL/egl.h>

class RenderEngine {
public:
    bool Initialize(ANativeWindow* window) {
        nativeWindow_ = window;
        
        // 创建 EGL 上下文
        display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
        eglInitialize(display_, nullptr, nullptr);
        
        EGLConfig config;
        EGLint numConfigs;
        eglChooseConfig(display_, attribs, &config, 1, &numConfigs);
        
        surface_ = eglCreateWindowSurface(display_, config, nativeWindow_, nullptr);
        context_ = eglCreateContext(display_, config, EGL_NO_CONTEXT, contextAttribs);
        
        eglMakeCurrent(display_, surface_, surface_, context_);
        
        return true;
    }
    
    void RenderFrame() {
        // 如果使用 AMediaCodec 直接输出到 Surface这里可能不需要额外渲染
        // 或者使用 OpenGL ES 进行后处理
        
        eglSwapBuffers(display_, surface_);
    }
    
private:
    ANativeWindow* nativeWindow_ = nullptr;
    EGLDisplay display_ = nullptr;
    EGLSurface surface_ = nullptr;
    EGLContext context_ = nullptr;
};

特点:

  • 使用 ANativeWindow 作为渲染目标
  • 支持 OpenGL ES 后处理
  • 直接显示在 Android Surface 上

[9] 显示输出

技术方案Android Surface / SurfaceView

// 在 Java/Kotlin 层创建 SurfaceView
// 或使用 ANativeWindow 直接显示

// 在 C++ 层获取 Surface
ANativeWindow* GetOutputSurface() {
    // 从 Java 层传入 Surface转换为 ANativeWindow
    // 或直接使用 ANativeWindow
    return nativeWindow_;
}

延迟分析

阶段 操作 典型延迟 优化措施
1. 屏幕捕获 Desktop Duplication API 1-3ms GPU 纹理,零拷贝
2. 编码 H.264 硬件编码 2-5ms Media Foundation 硬件编码器
3. 协议封装 FlatBuffers 序列化 <1ms 零拷贝序列化
4. 网络传输 UDP 传输 1-20ms USB RNDIS 优先
5. 网络接收 UDP 接收 <1ms 高效 Socket 处理
6. 协议解析 FlatBuffers 反序列化 <1ms 零拷贝反序列化
7. 解码 H.264 硬件解码 2-5ms AMediaCodec 硬件解码器
8. 渲染 OpenGL ES / Surface 1-2ms GPU 渲染
9. 显示 Android Surface 输出 0-1ms 直接 Surface 显示
总计 端到端延迟 <30ms USB RNDIS 模式)

关键特性

  1. 零拷贝

    • Windows 端Desktop Duplication → D3D11 纹理 → 编码器GPU
    • Android 端:解码器 → SurfaceGPU无需 CPU 拷贝
  2. 硬件加速

    • WindowsMedia Foundation 硬件编码器
    • AndroidAMediaCodec 硬件解码器
  3. 自适应码率

    • 根据网络状况动态调整编码参数
    • 关键帧I-frame重传机制
  4. 多显示器支持

    • Windows 端支持选择特定显示器捕获
    • 支持多显示器扩展模式

实现优先级

  1. 第一阶段:基础功能

    • Windows 屏幕捕获Desktop Duplication API
    • Windows H.264 编码Media Foundation
    • Android H.264 解码AMediaCodec
    • Android Surface 显示
  2. 第二阶段:优化

    • 零拷贝优化
    • 帧率控制
    • 自适应码率
  3. 第三阶段:高级功能

    • 多显示器支持
    • 延迟优化
    • 错误恢复