startScreenCapture method
Future<void>
startScreenCapture(
{ - bool? autoAdjustNetworkQuality,
- bool forcePermissionCheck = false,
})
Implementation
Future<void> startScreenCapture({
bool? autoAdjustNetworkQuality,
bool forcePermissionCheck = false,
}) async {
if (_isDisposed) {
log('⚠️ Intento de iniciar captura en instancia disposed');
return;
}
if (_isInitializingStream) {
log('⏳ Inicialización de captura ya en progreso, ignorando llamada');
return;
}
if (_isStreaming) {
log('ℹ️ Captura de pantalla WebRTC ya activa');
return;
}
_isInitializingStream = true;
try {
_markActivity();
// 0. Ajuste dinámico de calidad según el tipo de red (WiFi, 4G, 3G, 2G)
if (autoAdjustNetworkQuality ?? autoAdjustQuality) {
await autoAdjustQualityForCurrentNetwork();
}
// 1. Verificación controlada de permisos
final perms = await ensurePermissions(
forceRefresh: forcePermissionCheck,
requestNotifications: true,
);
if (!perms.notifications) {
log('⚠️ Permiso de notificaciones no disponible, continuando intento de captura...');
}
// 2. Comprobar / solicitar MediaProjection
final isRunning = await NativeBridge.isScreenCaptureRunning();
if (!isRunning) {
final granted = await NativeBridge.requestMediaProjection();
if (!granted) {
log('❌ Permiso de captura de pantalla denegado por el usuario');
return;
}
await Future.delayed(const Duration(milliseconds: 200));
}
// 3. Iniciar servicio nativo foreground si no está corriendo
final isServiceRunning = await NativeBridge.isScreenCaptureRunning();
if (!isServiceRunning) {
final started = await NativeBridge.startScreenCapture();
if (!started) {
log('❌ No se pudo iniciar el servicio de captura');
return;
}
}
// 4. Crear PeerConnection si aún no existe
if (_peerConnection == null) {
if (turnServerIP != null && turnServerPort != null) {
await _testTurnConnectivity(turnServerIP!, turnServerPort!);
}
_peerConnection = await createPeerConnection(
{
'iceServers': [
if (turnServerIP != null && turnServerPort != null)
{
'urls': 'turn:$turnServerIP:$turnServerPort',
if (turnServerUsername != null) 'username': turnServerUsername,
if (turnServerCredential != null) 'credential': turnServerCredential,
},
{'urls': 'stun:stun.l.google.com:19302'},
{'urls': 'stun:stun1.l.google.com:19302'},
],
'iceTransportPolicy': 'all',
'bundlePolicy': 'max-bundle',
'rtcpMuxPolicy': 'require',
'sdpSemantics': 'unified-plan',
},
{
'mandatory': {},
'optional': [
{'DtlsSrtpKeyAgreement': true},
],
},
);
_peerConnection!.onIceCandidate = (candidate) {
if (candidate.candidate == null || candidate.candidate!.isEmpty) return;
_markActivity();
_sendMessage({'type': 'ice-candidate', 'candidate': candidate.toMap()});
};
_peerConnection!.onConnectionState = (state) {
log('🔗 Estado WebRTC: $state');
if (state == RTCPeerConnectionState.RTCPeerConnectionStateFailed) {
_handleConnectionFailure();
} else if (state == RTCPeerConnectionState.RTCPeerConnectionStateConnected) {
_markActivity();
}
};
}
// 5. Capturar pantalla con constraints parametrizados
_localStream = await navigator.mediaDevices.getDisplayMedia({
'video': {
'width': videoWidth,
'height': videoHeight,
'frameRate': videoFrameRate,
},
'audio': false,
});
// 6. Agregar tracks al PeerConnection
for (final track in _localStream!.getTracks()) {
_peerConnection!.addTrack(track, _localStream!);
}
// 7. Crear y enviar oferta WebRTC
final offer = await _peerConnection!.createOffer({
'offerToReceiveAudio': false,
'offerToReceiveVideo': true,
});
await _peerConnection!.setLocalDescription(offer);
_sendMessage({'type': 'webrtc-offer', 'sdp': offer.toMap()});
_markActivity();
_isStreaming = true;
log('✅ Captura de pantalla y oferta WebRTC iniciadas exitosamente');
} catch (e) {
log('❌ Error al iniciar captura: $e');
await stopScreenCapture();
} finally {
_isInitializingStream = false;
}
}