更新文档

This commit is contained in:
huanglinhuan
2025-12-18 21:30:08 +08:00
parent 91ae52eeb3
commit 30f45f8397
2 changed files with 536 additions and 5 deletions

View File

@@ -0,0 +1,528 @@
# 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推荐**
```cpp
// 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 硬件编码器**
```cpp
// 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] 协议封装层
```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 <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**
```cpp
// 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**
```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 端:解码器 → 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. **第三阶段**:高级功能
- [ ] 多显示器支持
- [ ] 延迟优化
- [ ] 错误恢复