如何在Flutter中为Android和iOS集成带画中画的WebRTC视频通话
解决方案:Flutter WebRTC 视频通话画中画实现
针对你的Flutter跨平台(Android/iOS)WebRTC视频通话画中画需求,推荐结合flutter_webrtc(主流WebRTC插件)+原生画中画(PiP)API实现,这比第三方PiP插件适配WebRTC流更可靠,分平台具体步骤如下:
Android 端实现
配置Manifest文件
在android/app/src/main/AndroidManifest.xml中修改主Activity属性,添加PiP支持:<activity android:name=".MainActivity" android:supportsPictureInPicture="true" android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation" android:launchMode="singleTop"> <!-- 保留原有配置 --> </activity>同时添加必要权限:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>Flutter端调用原生PiP
通过MethodChannel触发原生代码,传递RTCVideoRenderer的Surface标识:import 'package:flutter/services.dart'; import 'package:flutter_webrtc/flutter_webrtc.dart'; class PipManager { static const MethodChannel _channel = MethodChannel('pip_channel'); static Future<void> enterPipMode(RTCVideoRenderer renderer) async { final surfaceId = renderer.textureId; await _channel.invokeMethod('enterPip', {'surfaceId': surfaceId}); } }Android原生实现PiP逻辑
在MainActivity.kt中处理MethodChannel调用,启动PiP模式:import android.app.PictureInPictureModeCompat import android.os.Bundle import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel class MainActivity : FlutterActivity() { private val CHANNEL = "pip_channel" override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> if (call.method == "enterPip") { val surfaceId = call.argument<Int>("surfaceId") ?: run { result.error("INVALID_ARG", "Surface ID missing", null) return@setMethodCallHandler } // 获取WebRTC渲染用的SurfaceView val surfaceView = flutterEngine.platformViewsController.getPlatformViewById(surfaceId) // 配置PiP窗口比例 val aspectRatio = android.util.Rational(16, 9) val params = PictureInPictureModeCompat.Builder() .setAspectRatio(aspectRatio) .build() // 进入PiP模式 enterPictureInPictureMode(params) result.success(null) } else { result.notImplemented() } } } }
iOS 端实现
配置Info.plist
添加PiP及视频通话相关配置到ios/Runner/Info.plist:<key>UIBackgroundModes</key> <array> <string>voip</string> <!-- 后台维持视频通话需要 --> </array> <key>NSCameraUsageDescription</key> <string>需要访问摄像头进行视频通话</string> <key>NSMicrophoneUsageDescription</key> <string>需要访问麦克风进行语音通话</string> <key>UIPictureInPictureEnabled</key> <true/>Flutter端调用原生PiP
复用Android端的PipManager类,传递RTCVideoRenderer的textureId:static Future<void> enterPipMode(RTCVideoRenderer renderer) async { final textureId = renderer.textureId; await _channel.invokeMethod('enterPip', {'textureId': textureId}); }iOS原生实现PiP逻辑
在AppDelegate.swift中处理MethodChannel调用,启动PiP:import UIKit import Flutter import WebRTC @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { private let CHANNEL = "pip_channel" private var pipController: AVPictureInPictureController? override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { guard let controller = window?.rootViewController as? FlutterViewController else { return super.application(application, didFinishLaunchingWithOptions: launchOptions) } let channel = FlutterMethodChannel(name: CHANNEL, binaryMessenger: controller.binaryMessenger) channel.setMethodCallHandler { [weak self] call, result in guard let self = self else { return } if call.method == "enterPip" { guard let textureId = call.arguments as? Int else { result.error("INVALID_ARG", "Texture ID missing", nil) return } // 获取WebRTC的渲染视图 guard let videoView = controller.platformViewsController.view(for: textureId) as? RTCEAGLVideoView, let videoLayer = videoView.layer else { result.error("NO_LAYER", "Video layer not found", nil) return } // 配置PiP内容源 let contentSource = AVPictureInPictureController.ContentSource( activeVideoLayer: videoLayer, contentSourceRect: videoLayer.bounds ) // 初始化并启动PiP self.pipController = AVPictureInPictureController(contentSource: contentSource) self.pipController?.delegate = self if self.pipController?.isPictureInPictureSupported ?? false { self.pipController?.startPictureInPicture() result.success(nil) } else { result.error("NOT_SUPPORTED", "PiP not supported on this device", nil) } } else { result.notImplemented() } } GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } } extension AppDelegate: AVPictureInPictureControllerDelegate { func pictureInPictureControllerDidStopPictureInPicture(_ controller: AVPictureInPictureController) { pipController = nil } }
替代方案:适配pip_flutter插件
如果你想继续使用pip_flutter,可以尝试将WebRTC视频帧转换为VideoPlayerController可识别的流,但这种方式会有一定延迟,仅适合非实时要求极高的场景:
- 通过
flutter_webrtc的RTCVideoRenderer监听帧回调,将每一帧的CVPixelBuffer(iOS)或Bitmap(Android)编码为视频流 - 将编码后的流作为
VideoPlayerController的数据源,再传入pip_flutter的播放器
但更推荐第一种原生PiP结合flutter_webrtc的方案,实时性和稳定性更优。
内容的提问来源于stack exchange,提问作者Arbaz Shaikh
相关产品推荐
相关产品推荐

