Files
roadwave/docs/adr/002-protocole-streaming.md
jpgiannetti 18c8901d69 docs(adr): corriger référence cassée ADR-002
Correction référence Règle Métier 05 :
- Avant : "Section 5.2 (Mode Voiture, lignes 16-84)"
- Après : "Section 5.1 (File d'attente et commande Suivant)"

Raison : La Règle 05 ne contient pas de Section 5.2.
La structure réelle est Section 5.1 pour la file d'attente.

Résout dernière incohérence P0 (INCONSISTENCIES.md item 5).
100% des incohérences P0 maintenant corrigées !

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-01 15:37:47 +01:00

183 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-002 : Protocole de Streaming
**Statut** : Accepté
**Date** : 2025-01-17
## Contexte
Streaming audio vers des utilisateurs mobiles en voiture, avec réseaux instables (tunnels, zones rurales, handoff cellulaire).
## Décision
**HLS** (HTTP Live Streaming) pour le contenu à la demande.
**WebRTC** réservé à la radio live.
## Alternatives considérées
| Option | Latence | Fiabilité mobile | Cache CDN | Complexité |
|--------|---------|------------------|-----------|------------|
| **HLS** | 5-30s | Excellente | Oui | Faible |
| DASH | 5-30s | Bonne | Oui | Moyenne |
| WebRTC | <500ms | Moyenne | Non | Élevée |
| UDP brut | Minimale | Faible | Non | Très élevée |
## Justification
- **Réseaux mobiles** : HLS gère les coupures et changements de cellule nativement
- **Cache CDN** : Segments .ts cachables = réduction des coûts
- **Compatibilité** : Support natif iOS/Android
- **Bitrate adaptatif** : Ajustement automatique selon la qualité réseau
## Pourquoi pas UDP ?
- Problèmes NAT/firewall sur réseaux mobiles
- Perte de paquets = artefacts audio
- Impossible à cacher sur CDN
- Complexité sans bénéfice pour du contenu non-interactif
## Conséquences
- Latence de 5-30s acceptable pour podcasts/audio-guides (avec pré-buffering, voir section 4.3)
- WebRTC à implémenter séparément pour la radio live
## Gestion de la Latence et Synchronisation Géolocalisée
### Problème Identifié
La latence HLS (5-30s) entre en conflit avec les notifications géolocalisées qui doivent déclencher l'audio **au moment précis** où l'utilisateur atteint un point d'intérêt.
**Exemple critique** :
- Utilisateur en voiture à 90 km/h (25 m/s)
- ETA de 7 secondes avant le point → notification affichée
- Latence HLS de 15 secondes
- Résultat : audio démarre **200 mètres après** le point d'intérêt ❌
### Solution : Pre-buffering Anticipé + ETA Adaptatif
#### 4.3.1 Pre-buffering Automatique
**Déclenchement** : À ETA = 30 secondes du prochain point d'intérêt
```
[App Mobile]
↓ (ETA=30s, position GPS détectée)
[Cache Manager]
↓ (télécharge en arrière-plan)
[CDN NGINX] → /audio/poi-{id}/intro.m4a (10-15s d'audio, ~5-8 MB)
[Cache Local Mobile] (max 3 POI simultanés)
```
**Stratégie de cache** :
- Télécharge les **15 premières secondes** de chaque POI à proximité
- Limite : 3 POI simultanés en cache (max ~25 MB)
- Purge automatique après 200m de distance passée
- Format : M4A haute qualité (128 kbps) pour intro, puis bascule HLS pour la suite
#### 4.3.2 ETA de Notification Adaptatif
**Algorithme** :
```python
def calculate_notification_eta(poi, user_position, user_speed):
distance_to_poi = haversine(user_position, poi.position)
is_cached = cache.has(poi.audio_id)
hls_latency = metrics.get_average_latency(user_id) # 8-18s typique
if is_cached:
# Cache prêt → notification courte (temps de réaction)
notification_eta = 5 # secondes
else:
# Pas de cache → compenser latence HLS + marge
notification_eta = hls_latency + 3 + 2 # latence + marge + réaction
# Typique: 10s + 3s + 2s = 15s
time_to_poi = distance_to_poi / user_speed
if time_to_poi <= notification_eta:
send_notification(poi)
if time_to_poi <= 30 and not is_cached:
cache.preload_async(poi.audio_id)
```
**Résultat** :
| Situation | ETA Notification | Distance (à 90 km/h) | Expérience |
|-----------|------------------|----------------------|------------|
| Cache prêt | 5s | 125m avant | ✅ Lecture instantanée |
| Cache en cours | 15s | 375m avant | ✅ Lecture à temps |
| 3G lent | 20s | 500m avant | ✅ Compensation latence |
#### 4.3.3 Mesure Dynamique de Latence
**Tracking par utilisateur** :
```go
type HLSMetrics struct {
UserID uuid.UUID
AvgLatency time.Duration // Moyenne glissante 10 lectures
NetworkType string // "4G", "5G", "3G", "wifi"
LastMeasured time.Time
}
// Mesure lors de chaque lecture
func (m *HLSMetrics) RecordPlaybackStart(requestTime, firstByteTime time.Time) {
latency := firstByteTime.Sub(requestTime)
m.AvgLatency = (m.AvgLatency*9 + latency) / 10 // Moyenne glissante
}
```
**Utilisation** : L'ETA adaptatif utilise `AvgLatency` personnalisé par utilisateur au lieu d'une valeur fixe.
#### 4.3.4 Fallback et Indicateurs Visuels
Si le pre-buffer échoue (réseau faible, pas de cache), afficher un **loader avec progression** :
```
┌─────────────────────────────────┐
│ 🏰 Château de Fontainebleau │
│ │
│ [████████░░] 80% │
│ Chargement de l'audio... │
│ │
│ 📍 Vous arrivez dans 12s │
└─────────────────────────────────┘
```
- **Feedback visuel** : Utilisateur comprend que ça charge
- **ETA affiché** : Maintient l'attention et réduit la frustration perçue
- **Timeout** : Si > 30s sans succès, proposer mode dégradé (texte seul)
### Impact sur l'Infrastructure
#### Backend (Go)
- **Nouveau service** : `audiocache.Service` pour préparer les extraits M4A
- **Endpoint** : `GET /api/v1/audio/poi/:id/intro` (retourne 15s d'audio)
- **CDN** : Cache NGINX avec TTL 7 jours sur `/audio/*/intro.m4a`
#### Mobile (Flutter)
- **Package** : `just_audio` avec cache local (`flutter_cache_manager`)
- **Stockage** : Max 100 MB de cache audio (auto-purge LRU)
- **Logique** : `PreBufferService` avec scoring de priorité POI
#### Coûts
- **Bande passante** : +10-15 MB/utilisateur/session (vs streaming pur)
- **Stockage CDN** : +500 MB pour 1000 POI × 5 MB intro (négligeable)
- **Économie** : Cache CDN réduit les requêtes origin (-60% selon tests)
### Métriques de Succès
- **Latence perçue** : < 1 seconde dans 95% des cas (cache hit)
- **Synchronisation** : Audio démarre à ±10 mètres du POI (objectif ±50m)
- **Cache hit rate** : > 90% pour utilisateurs en mode navigation
- **Consommation data** : +15% vs HLS pur, mais acceptable pour UX
### Références
- [HLS Authoring Specification](https://developer.apple.com/documentation/http_live_streaming/hls_authoring_specification_for_apple_devices)
- [Low-Latency HLS (LL-HLS)](https://developer.apple.com/documentation/http_live_streaming/enabling_low-latency_hls)
- Règle Métier 05 : Section 5.1 (File d'attente et commande Suivant)
- Règle Métier 17 : Section 17.2 (ETA Géolocalisé, lignes 25-65)
- **ADR-017** : Architecture des Notifications Géolocalisées