← Tous les articles
May 9, 2026

🎬 Construire un mini Netflix avec Go, gRPC et HLS — architecture moderne de streaming vidéo

Développement Web #python #Docker #docker-compose #Devops #Golang

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.

Thiébault Michaël ©