# screen_mirroring **Repository Path**: wangjunjx8868/screen_mirroring ## Basic Information - **Project Name**: screen_mirroring - **Description**: flutter 投屏插件 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2026-04-13 - **Last Updated**: 2026-06-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # flutter_media_cast 一个支持 DLNA 投屏、Android Miracast 连接验证,以及 iOS AirPlay 媒体播放与系统投屏入口的 Flutter 插件,兼容 Android 与 iOS。 ## 功能特性 - 在局域网内发现 DLNA 投屏设备(Android & iOS) - 将视频、音频、图片投屏到 DLNA 设备 - Android 支持 Miracast 设备发现、连接验证和手动断开 - 实时推送投屏状态与播放进度事件 - 播放控制:播放、暂停、停止、跳转 - 音量控制,iOS 端自动同步系统音量 - iOS 支持拉起系统 AirPlay 设备选择器 - iOS 支持 AirPlay 媒体准备、播放、暂停、停止与状态监听 - Android 暂不支持 AirPlay - 字幕支持(SRT、SSA/ASS、VTT、SMI) - 根据 URL 后缀自动识别 MIME 类型 ## 平台支持 | 功能 | Android | iOS | |----------------------|---------|-----| | DLNA 设备发现 | ✅ | ✅ | | DLNA 投屏 | ✅ | ✅ | | Miracast 设备发现 | ✅ | ❌ | | Miracast 连接验证 | ✅ | ❌ | | AirPlay 系统选择器 | ❌ | ✅ | | AirPlay 媒体播放 | ❌ | ✅ | | 音量同步 | ✅ | ✅ | | 字幕支持 | ✅ | ✅ | ## 安装 在 `pubspec.yaml` 中添加依赖: ```yaml dependencies: flutter_media_cast: git: url: https://gitee.com/sirli369/screen_mirroring ``` ### Android 配置 在 `android/app/src/main/AndroidManifest.xml` 中添加权限: ```xml ``` ### iOS 配置 在 `ios/Runner/Info.plist` 中添加以下配置: ```xml NSLocalNetworkUsageDescription 此应用需要访问本地网络以发现投屏设备 NSBonjourServices _upnp._tcp. NSAppTransportSecurity NSAllowsLocalNetworking ``` ## 快速上手 ### 初始化 ```dart import 'package:flutter_media_cast/flutter_media_cast.dart'; await FlutterMediaCast.init(); ``` ### 发现设备 ```dart // 监听发现的设备列表 FlutterMediaCast.deviceStream.listen((List devices) { print('发现 ${devices.length} 个设备'); }); // 开始搜索(超时后自动停止) await FlutterMediaCast.startDiscovery( timeout: const Duration(seconds: 15), ); // 手动停止搜索 await FlutterMediaCast.stopDiscovery(); ``` 如需只搜索特定协议,可以传入 `protocols`: ```dart await FlutterMediaCast.startDiscovery( protocols: const [CastProtocol.dlna], timeout: const Duration(seconds: 15), ); ``` ### 连接并投屏 ```dart // 连接设备 final CastDevice device = devices.first; final bool connected = await FlutterMediaCast.connect(device); if (connected) { // 开始投屏 final bool success = await FlutterMediaCast.castMedia( MediaItem( url: 'http://example.com/video.mp4', title: '我的视频', ), ); } ``` ### Miracast 连接验证 ```dart final miracastDevices = devices.where((device) => device.protocol == CastProtocol.miracast); final connected = await FlutterMediaCast.connect(miracastDevices.first); if (connected) { print('Miracast 已连接,请手动控制投屏/镜像'); } ``` 说明: - Miracast 在当前版本中用于设备发现、连接验证和手动断开 - Miracast 不等同于 DLNA,当前版本不承诺媒体 URL 直投 - 若要停止 Miracast 连接,调用 `FlutterMediaCast.disconnect()` ### AirPlay 快速接入 ```dart final media = MediaItem( url: 'https://example.com/video.mp4', title: 'AirPlay 测试视频', ); final prepared = await FlutterMediaCast.prepareAirPlayMedia(media); if (!prepared) { print('AirPlay 媒体准备失败'); return; } final opened = await FlutterMediaCast.showAirPlayRoutePicker(); if (!opened) { print('当前平台不支持,或未能拉起 AirPlay 设备选择器'); return; } await FlutterMediaCast.playAirPlay(); ``` 说明: - AirPlay 当前仅支持 iOS 真机,模拟器通常无法拉起系统路由选择器 - 推荐先调用 `prepareAirPlayMedia()`,再调用 `showAirPlayRoutePicker()` - 选择外放设备后,可通过 `playAirPlay()` / `pauseAirPlay()` / `stopAirPlay()` 控制播放 ### 监听 AirPlay 状态 ```dart FlutterMediaCast.airPlayStateStream.listen((AirPlayState state) { print('AirPlay 状态: ${state.name}'); }); ``` ### 投屏带字幕的视频 ```dart await FlutterMediaCast.castMedia( MediaItem( url: 'http://example.com/video.mp4', title: '我的视频', subtitleUrl: 'http://example.com/subtitle.srt', ), ); ``` ### 监听投屏状态 ```dart FlutterMediaCast.castStateStream.listen((CastState state) { switch (state) { case CastState.idle: print('空闲'); case CastState.discovering: print('搜索中...'); case CastState.connecting: print('连接中...'); case CastState.connected: print('已连接'); case CastState.casting: print('投屏中'); case CastState.error: print('发生错误'); } }); ``` ### 监听播放进度 ```dart FlutterMediaCast.positionStream.listen((MediaPosition pos) { print('${pos.position}ms / ${pos.duration}ms(${(pos.progress * 100).toStringAsFixed(1)}%)'); }); // 监听播放完成 FlutterMediaCast.playbackCompletedStream.listen((_) { print('播放完成'); }); ``` ### 播放控制 ```dart await FlutterMediaCast.play(); // 播放 await FlutterMediaCast.pause(); // 暂停 await FlutterMediaCast.stop(); // 停止 await FlutterMediaCast.seekTo(30000); // 跳转到 30 秒(毫秒) await FlutterMediaCast.setVolume(0.8); // 设置音量(0.0 ~ 1.0) ``` ### 断开连接与释放资源 ```dart await FlutterMediaCast.disconnect(); // 不再使用插件时调用 await FlutterMediaCast.dispose(); ``` ## API 参考 ### FlutterMediaCast | 方法 / 属性 | 说明 | |----------------------------------------|-------------------------------------------------| | `init()` | 初始化插件 | | `startDiscovery({protocols, timeout})` | 开始搜索投屏设备 | | `stopDiscovery()` | 停止搜索设备 | | `deviceStream` | 已发现设备列表的事件流 | | `castStateStream` | 投屏状态的事件流 | | `positionStream` | 播放进度的事件流(每秒更新一次) | | `playbackCompletedStream` | 播放进度达到 99% 时触发的事件流 | | `connect(device)` | 连接到指定设备,返回 `bool` | | `disconnect()` | 断开当前连接 | | `castMedia(media)` | 投屏媒体资源,返回 `bool` | | `play()` | 恢复播放 | | `pause()` | 暂停播放 | | `stop()` | 停止播放 | | `seekTo(positionMs)` | 跳转到指定位置(毫秒) | | `setVolume(volume)` | 设置音量(0.0 ~ 1.0) | | `getConnectedDevice()` | 获取当前连接的设备,未连接时返回 `null` | | `getCastState()` | 获取当前投屏状态 | | `getAvailableProtocols()` | 获取支持的协议列表 | | `showAirPlayRoutePicker()` | 拉起 iOS 系统 AirPlay 设备选择器,Android 返回 `false` | | `prepareAirPlayMedia(media)` | 为 AirPlay 准备媒体资源,返回 `bool` | | `airPlayStateStream` | AirPlay 状态事件流 | | `playAirPlay()` | 开始或恢复 AirPlay 播放 | | `pauseAirPlay()` | 暂停 AirPlay 播放 | | `stopAirPlay()` | 停止 AirPlay 播放 | | `dispose()` | 释放所有资源 | ### CastDevice | 属性 | 类型 | 说明 | |----------------|----------------|-----------------------| | `id` | `String` | 设备唯一标识 | | `name` | `String` | 设备名称 | | `protocol` | `CastProtocol` | 投屏协议(`airplay`、`dlna`、`miracast`) | | `ipAddress` | `String?` | 设备 IP 地址 | | `port` | `int?` | 设备端口号 | | `manufacturer` | `String?` | 设备制造商 | | `model` | `String?` | 设备型号 | | `location` | `String?` | UPnP 设备描述文件地址 | ### MediaItem | 属性 | 类型 | 说明 | |--------------------|-------------|--------------------------------------| | `url` | `String` | 媒体地址(HTTP/HTTPS) | | `title` | `String` | 媒体标题 | | `mediaType` | `MediaType` | 媒体类型:`video`、`audio`、`image` | | `mimeType` | `String?` | MIME 类型(不填则根据 URL 自动识别) | | `thumbnailUrl` | `String?` | 缩略图地址 | | `artist` | `String?` | 艺术家 / 作者 | | `album` | `String?` | 专辑名称 | | `subtitleUrl` | `String?` | 字幕文件地址 | | `subtitleMimeType` | `String?` | 字幕 MIME 类型(不填则自动识别) | ### 枚举类型 ```dart // 投屏协议 enum CastProtocol { airplay, dlna, miracast } // 投屏状态 enum CastState { idle, discovering, connecting, connected, casting, error } // AirPlay 状态 enum AirPlayState { idle, preparing, ready, playing, paused, stopped, error } // 媒体类型 enum MediaType { video, audio, image } ``` ## 环境要求 - Flutter `>=3.3.0` - Dart `^3.7.2` - Android:minSdkVersion 21+ - iOS:13.0+