diff --git a/DEVELOPMENT_PLAN.md b/DEVELOPMENT_PLAN.md index b8766ed..c95f04b 100644 --- a/DEVELOPMENT_PLAN.md +++ b/DEVELOPMENT_PLAN.md @@ -59,14 +59,17 @@ **目标**:实现各平台的显示捕获和渲染功能 #### 2.1 Android 平台实现 -- [ ] **显示捕获** - - MediaProjection API 集成 - - ImageReader 帧获取 +- [ ] **显示捕获**(纯 C++ NDK Native API 实现) + - AImageReader(NDK ImageReader API)集成 + - ANativeWindow 窗口操作 + - AHardwareBuffer 零拷贝帧获取 + - VirtualDisplay 创建(需要 MediaProjection token,仅权限请求) - 屏幕分辨率检测 - 帧率控制 + - GPU 缓冲区直接访问(零拷贝) -- [ ] **摄像头捕获** - - Camera2 API / NDK Camera API 集成 +- [ ] **摄像头捕获**(纯 C++ NDK Native API 实现) + - NDK Camera API(ACameraManager, ACameraDevice)集成 - 摄像头设备枚举 - 视频流捕获 - 摄像头参数控制(分辨率、帧率、对焦等) diff --git a/docs/VIDEO_FLOW_WINDOWS_TO_ANDROID.md b/docs/VIDEO_FLOW_WINDOWS_TO_ANDROID.md new file mode 100644 index 0000000..69f1bce --- /dev/null +++ b/docs/VIDEO_FLOW_WINDOWS_TO_ANDROID.md @@ -0,0 +1,528 @@ +# Windows Host → Android Client 视频流向详解 + +本文档详细说明 Windows 设备作为 Host,Android 设备作为 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(推荐)** + +```cpp +// platforms/windows/src/capture/screen_capture.cpp +#include +#include + +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 API(Windows 8+) +- 直接获取 GPU 纹理,零拷贝 +- 支持多显示器 +- 性能最优 + +**备选方案:GDI BitBlt** +- 适用于较旧的 Windows 版本 +- 需要 CPU 拷贝,性能较低 + +#### [2] 编码层 + +**技术方案:Media Foundation H.264 硬件编码器** + +```cpp +// platforms/windows/src/codec/windows_h264_encoder.cpp +#include +#include +#include + +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& 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] 协议封装层 + +```cpp +// 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] 网络传输层 + +```cpp +// 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] 网络接收层 + +```cpp +// 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] 协议解析层 + +```cpp +// 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 硬件解码器** + +```cpp +// platforms/android/src/render/render_engine.cpp +#include +#include + +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** + +```cpp +// platforms/android/src/render/render_engine.cpp +#include +#include + +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** + +```cpp +// 在 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 端:解码器 → Surface(GPU),无需 CPU 拷贝 + +2. **硬件加速**: + - Windows:Media Foundation 硬件编码器 + - Android:AMediaCodec 硬件解码器 + +3. **自适应码率**: + - 根据网络状况动态调整编码参数 + - 关键帧(I-frame)重传机制 + +4. **多显示器支持**: + - Windows 端支持选择特定显示器捕获 + - 支持多显示器扩展模式 + +## 实现优先级 + +1. **第一阶段**:基础功能 + - [ ] Windows 屏幕捕获(Desktop Duplication API) + - [ ] Windows H.264 编码(Media Foundation) + - [ ] Android H.264 解码(AMediaCodec) + - [ ] Android Surface 显示 + +2. **第二阶段**:优化 + - [ ] 零拷贝优化 + - [ ] 帧率控制 + - [ ] 自适应码率 + +3. **第三阶段**:高级功能 + - [ ] 多显示器支持 + - [ ] 延迟优化 + - [ ] 错误恢复 +