# carmon
**Repository Path**: msuno/carmon
## Basic Information
- **Project Name**: carmon
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-04-18
- **Last Updated**: 2026-06-21
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 双屏异显投屏方案
基于 CarLife 源码分析实现的双屏异显投屏方案,支持后台投屏且主屏幕操作不影响投屏显示。
## 核心原理
### 1. 屏幕捕获 (MediaProjection)
- 使用 Android 5.0+ 提供的 `MediaProjection` API 捕获屏幕内容
- 系统级捕获,不影响主屏幕正常操作
- 需要用户授权(系统弹窗)
### 2. 虚拟显示 (VirtualDisplay)
- 创建 `VirtualDisplay` 将捕获的内容输出到指定 Surface
- 支持自定义分辨率、DPI
- 可以输出到 SurfaceView、TextureView 或编码器
### 3. 副屏显示
- **Presentation 模式**:使用 Android 原生 `Presentation` 类在副屏显示
- 优点:支持触摸事件回传、生命周期管理完善
- 缺点:需要副屏支持 Presentation
- **悬浮窗模式**:使用 `WindowManager` 在副屏创建悬浮窗
- 优点:兼容性好,适用于各种副屏
- 缺点:需要悬浮窗权限
## 文件说明
### 1. SimpleDualScreenProjection.kt(推荐)
简化版投屏方案,直接使用 VirtualDisplay 输出到 SurfaceView,无需编解码,延迟低。
**使用场景**:
- 单设备双屏(如车机+仪表屏)
- 对延迟要求高的场景
- 快速集成
**核心代码**:
```kotlin
// 初始化
val projection = SimpleDualScreenProjection.getInstance(context)
projection.init(resultCode, data)
// 开始投屏
projection.start(targetDisplay, width, height, dpi)
// 停止投屏
projection.stop()
```
### 2. DualScreenProjection.kt
完整版投屏方案,包含视频编码/解码,支持网络传输。
**使用场景**:
- 跨设备投屏
- 需要压缩传输的场景
- 需要录制屏幕内容
**核心流程**:
```
主屏幕 -> MediaProjection -> VirtualDisplay -> MediaCodec(编码) -> 网络/本地 -> MediaCodec(解码) -> 副屏Surface
```
### 3. ProjectionService.kt
后台服务,保持投屏连接,提供通知栏控制。
**功能**:
- 前台服务保活
- 通知栏快捷控制
- 后台持续投屏
## 使用步骤
### 1. 添加权限
```xml
```
### 2. 请求屏幕捕获权限
```kotlin
val projectionManager = getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
startActivityForResult(projectionManager.createScreenCaptureIntent(), REQUEST_CODE)
```
### 3. 初始化并启动投屏
```kotlin
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK) {
val projection = SimpleDualScreenProjection.getInstance(this)
projection.init(resultCode, data!!)
// 获取副屏
val displays = projection.getSecondaryDisplays()
if (displays.isNotEmpty()) {
projection.start(displays[0])
}
}
}
```
## 技术要点
### 1. 主屏幕操作不影响投屏
- `MediaProjection` 是系统级服务,捕获屏幕不会干扰用户操作
- `VirtualDisplay` 在后台运行,不需要前台 Activity
### 2. 副屏检测
```kotlin
val displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
val secondaryDisplays = displayManager.displays.filter {
it.displayId != Display.DEFAULT_DISPLAY
}
```
### 3. 分辨率适配
```kotlin
// 获取副屏分辨率
val metrics = DisplayMetrics()
display.getRealMetrics(metrics)
val width = metrics.widthPixels
val height = metrics.heightPixels
```
### 4. 性能优化
- 使用 `FLAG_HARDWARE_ACCELERATED` 启用硬件加速
- 根据副屏分辨率调整投屏分辨率
- 使用合适的 DPI 避免界面元素过大/过小
## 与 CarLife 方案对比
| 特性 | CarLife 方案 | 本方案 |
|------|-------------|--------|
| 协议 | 自定义协议 + H.264 | MediaProjection + VirtualDisplay |
| 编码 | 手机端编码 | 系统级捕获(可选编码) |
| 解码 | 车机端解码 | 直接显示(或可选解码) |
| 延迟 | 较高(编解码+传输) | 较低(直接显示) |
| 兼容性 | 需要 CarLife 协议支持 | 需要 Android 5.0+ |
| 适用场景 | 手机投屏到车机 | 单设备多屏、本地双屏 |
## 注意事项
1. **权限要求**:
- 屏幕捕获需要用户每次授权
- 悬浮窗需要 `SYSTEM_ALERT_WINDOW` 权限
2. **系统限制**:
- 部分系统可能限制后台服务
- 省电模式可能影响投屏
3. **性能考虑**:
- 高分辨率投屏会消耗更多资源
- 建议根据设备性能调整分辨率
4. **安全性**:
- 屏幕捕获会捕获所有屏幕内容
- 敏感界面建议暂停投屏
## 扩展功能
### 1. 触摸回传
在 Presentation 模式下,可以通过 `onTouchEvent` 将副屏触摸事件回传到主屏幕。
### 2. 音频同步
使用 `AudioRecord` 捕获系统音频,通过 `AudioTrack` 在副屏播放。
### 3. 远程投屏
将编码后的视频流通过网络传输到远程设备显示。
### 4. 画面裁剪
通过 `VirtualDisplay` 的参数设置,可以只投屏部分区域。
## 参考
- [CarLife 源码分析](./carlife_source/)
- [Android MediaProjection API](https://developer.android.com/reference/android/media/projection/MediaProjection)
- [Android VirtualDisplay API](https://developer.android.com/reference/android/hardware/display/VirtualDisplay)
- [Android Presentation API](https://developer.android.com/reference/android/app/Presentation)