# 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)