Aujourd’hui, on va décortiquer une architecture complète de streaming vidéo inspirée de plateformes comme Netflix, mais simplifiée pour comprendre les briques essentielles :
- gRPC pour le transport des vidéos
- Go pour les microservices
- FFmpeg pour le transcodage
- HLS pour le streaming adaptatif
- Un gateway HTTP pour exposer le contenu au navigateur
L’objectif : partir d’un fichier vidéo brut et arriver à un streaming lisible dans un navigateur.
🧠 Vue d’ensemble de l’architecture
Client Web (HLS.js)
↓
Video Gateway (Go HTTP)
↓
gRPC Video Service
↓
Storage (fichiers bruts)
↓
FFmpeg (normalisation + HLS)
↓
Segments .ts + playlist .m3u8
🧩 1. Le service gRPC vidéo (backend source)
Le rôle de ce service est simple : fournir les fichiers vidéo bruts en streaming chunké.
📦 Proto
service VideoService {
rpc StreamVideo(VideoRequest) returns (stream VideoChunk);
}
message VideoRequest {
string name = 1;
}
message VideoChunk {
bytes data = 1;
}
🧠 Implémentation Go
func (s *server) StreamVideo(req *pb.VideoRequest, stream pb.VideoService_StreamVideoServer) error {
file, err := os.Open("/data/videos/" + req.Name)
if err != nil {
return err
}
defer file.Close()
buf := make([]byte, 32*1024)
for {
n, err := file.Read(buf)
if err == io.EOF {
break
}
if err != nil {
return err
}
stream.Send(&pb.VideoChunk{
Data: buf[:n],
})
}
return nil
}
👉 Ici on ne “télécharge pas une vidéo”, on la stream en chunks via gRPC.
🚪 2. Video Gateway (API HTTP + FFmpeg)
C’est le cœur du système :
- Récupère la vidéo via gRPC
- Normalise la vidéo (FFmpeg)
- Génère du HLS (.m3u8 + segments .ts)
📥 2.1 Download via gRPC
func downloadVideo(ctx context.Context, id string) (string, error) {
output := "storage/" + id + ".mpg"
stream, _ := videoClient.StreamVideo(ctx, &pb.VideoRequest{
Name: id + ".mpg",
})
file, _ := os.Create(output)
defer file.Close()
for {
chunk, err := stream.Recv()
if err == io.EOF {
break
}
file.Write(chunk.Data)
}
return output, nil
}
🧼 2.2 Normalisation vidéo (important)
Corrige les problèmes classiques :
- Queue input is backward in time
- Non-monotonic DTS
func normalizeVideo(input, id string) (string, error) {
output := "storage/" + id + "_normalized.mp4"
cmd := exec.Command(
"ffmpeg",
"-y",
"-fflags", "+genpts",
"-i", input,
"-c:v", "libx264",
"-preset", "veryfast",
"-pix_fmt", "yuv420p",
"-c:a", "aac",
"-b:a", "128k",
"-af", "aresample=async=1",
"-avoid_negative_ts", "make_zero",
output,
)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return output, cmd.Run()
}
🎞️ 2.3 Génération HLS
func generateHLS(input, id string) error {
outputDir := "videos/" + id
os.MkdirAll(outputDir, 0755)
cmd := exec.Command(
"ffmpeg",
"-y",
"-i", input,
"-c:v", "libx264",
"-preset", "veryfast",
"-g", "48",
"-keyint_min", "48",
"-sc_threshold", "0",
"-c:a", "aac",
"-b:a", "128k",
"-f", "hls",
"-hls_time", "4",
"-hls_playlist_type", "vod",
"-hls_flags", "independent_segments",
"-hls_segment_filename", outputDir+"/seg_%03d.ts",
outputDir+"/index.m3u8",
)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
🌐 3. HTTP Gateway (serveur vidéo)
Expose la vidéo au navigateur :
/video/{id}/index.m3u8
Logique :
if HLS existe {
serve direct
} else {
download gRPC
normalize FFmpeg
generate HLS
}
🎥 4. Lecture côté navigateur
<video id="video" controls></video>
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<script>
const video = document.getElementById('video');
const hls = new Hls();
hls.loadSource("https://stream.thiebault.test/video/goldorak/index.m3u8");
hls.attachMedia(video);
</script>
🧠 5. Comment fonctionne le streaming vidéo (version corrigée)
Lorsqu’un utilisateur ouvre la vidéo, plusieurs étapes automatiques se produisent :
1️⃣ Demande du navigateur
/video/goldorak/index.m3u8
👉 C’est la playlist HLS.
2️⃣ Vérification du cache
- ✔ HLS existe → lecture immédiate
- ❌ n’existe pas → pipeline démarre
3️⃣ Téléchargement via gRPC (si absent)
Le fichier est streamé depuis le service :
Video Service → Video Gateway
👉 fichier reconstruit chunk par chunk
4️⃣ Normalisation FFmpeg
Corrige les vidéos instables :
goldorak.mpg → goldorak_normalized.mp4
5️⃣ Génération HLS
index.m3u8 seg_000.ts seg_001.ts ...
Chaque segment ≈ 4 secondes.
6️⃣ Lecture progressive
- le navigateur lit la playlist
- charge les segments un par un
- lecture immédiate
⚡ Résumé du pipeline
Browser ↓ HTTP Gateway ↓ (cache miss) gRPC download ↓ FFmpeg normalize ↓ HLS generation ↓ Streaming playback
🚀 Conclusion
Tu as construit une architecture de streaming complète :
- ✔ microservices gRPC
- ✔ pipeline FFmpeg
- ✔ HLS adaptatif
- ✔ gateway HTTP
- ✔ playback browser type Netflix
👉 C’est exactement une mini plateforme de streaming moderne.