Haben Sie sich jemals gewunscht, eine Fahrradroute vor der Fahrt in der Vorschau anzusehen? Wir haben ein automatisiertes System gebaut, das wunderschone Uberflugvideos fur jede Party-Route auf Party Onbici generiert und Radfahrern eine cinematische Vorschau dessen bietet, was sie auf ihrer Reise erwartet.

Die Herausforderung

Bei der Organisation oder Teilnahme an einem Radevent ist das Verstandnis der Route entscheidend. Statische Karten sind hilfreich, aber sie vermitteln nicht das Erlebnis, die Route tatsachlich zu fahren. Wir wollten den Nutzern eine Moglichkeit geben, die Route virtuell zu “uberfliegen”, bevor sie sich fur eine Fahrt entscheiden.

Unsere Losung: Headless Video-Rendering

Wir haben einen Node.js-Service gebaut, der einen headless Chrome-Browser mit Puppeteer ausfuhrt, die Route auf einer interaktiven MapLibre GL-Karte rendert und Frames erfasst, wahrend eine virtuelle Kamera entlang des Pfades fliegt. So funktioniert es:

1. Routen-Interpolation

Eine Fahrradroute kann Tausende von Koordinatenpunkten haben. Um ein flussiges Video mit 30 FPS fur eine 15-Sekunden-Dauer zu erstellen, benotigen wir genau 450 Frames. Wir verwenden distanzbasierte Interpolation, um die Route gleichmassig abzutasten:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// Calculate cumulative distances using Haversine formula
const distances = [0];
for (let i = 1; i < coordinates.length; i++) {
  const dist = haversineDistance(coordinates[i-1], coordinates[i]);
  totalDistance += dist;
  distances.push(totalDistance);
}

// Sample at exactly totalFrames points
for (let frame = 0; frame < totalFrames; frame++) {
  const targetDist = (frame / (totalFrames - 1)) * totalDistance;
  // Interpolate position at this distance...
}

2. Kamera-Animation mit Sanfter Ausrichtung

Einfach die Kamera in Fahrtrichtung zu richten, erzeugt ruckartige Bewegungen auf kurvigen Routen. Wir glatten die Kameraausrichtung mit einem gleitenden Durchschnitt uber 12 Frames, mit spezieller Behandlung fur den 360-Grad/0-Grad-Ubergang:

1
2
3
4
5
6
7
// Use sin/cos components for circular averaging
for (let j = i - smoothWindow; j <= i + smoothWindow; j++) {
  const rad = rawBearings[j] * Math.PI / 180;
  sinSum += Math.sin(rad);
  cosSum += Math.cos(rad);
}
const avgBearing = Math.atan2(sinSum/count, cosSum/count) * 180 / Math.PI;

3. MapLibre GL Rendering

Die Route wird auf einer schonen Stadia Maps-Basisschicht gerendert mit:

  • Einer hervorgehobenen Routenlinie mit weisser Umrandung fur Sichtbarkeit
  • Animiertem Kamerapositionsindikator
  • Start- (grun) und Zielmarkierungen (rot)
  • Optionalen 3D-Gebaudeextrusionen fur stadtische Gebiete

4. Frame-Erfassung und Video-Kodierung

Puppeteer erfasst jeden Frame als PNG-Screenshot, dann verwenden wir ffmpeg, um sie in ein WebM-Video mit VP9-Codec zu kodieren:

1
2
3
ffmpeg -framerate 30 -i frame_%05d.png \
  -c:v libvpx-vp9 -crf 20 -b:v 0 \
  route.webm

Technische Architektur

1
Django App → Celery Task → Node.js Renderer → S3 Storage
  1. Django lost die Videogenerierung aus, wenn eine Party erstellt oder aktualisiert wird
  2. Celery reiht die Aufgabe ein fur asynchrone Verarbeitung
  3. Der Node.js-Renderer fuhrt Puppeteer mit MapLibre GL aus
  4. ffmpeg kodiert die erfassten Frames
  5. Das Video wird hochgeladen zu S3 und der Party-Datensatz wird aktualisiert

Leistungsuberlegungen

AuflosungDauerRenderzeitDateigrosse
1280x72015s60-90s2-4 MB
1920x108015s90-120s4-8 MB

Fur die Produktion verwenden wir Docker-Container mit Software-Rendering (SwiftShader), um GPU-Abhangigkeiten zu vermeiden:

1
2
--use-angle=swiftshader
--enable-unsafe-swiftshader

Funktionen, die Wir Hinzugefugt Haben

  • Anpassbare Videoabmessungen - Quadratisches Format fur Social-Media-Sharing
  • Logo-Wasserzeichen - Versehen Sie Ihre Videos mit dem Logo Ihrer Organisation
  • Kameraneigung und Zoom - Passen Sie den Betrachtungswinkel und die Hohe an
  • Routenlinien-Styling - Benutzerdefinierte Farben und Breiten

Django-Integration

Aus dem Anwendungscode ist das Generieren eines Videos so einfach wie:

1
2
party = Party.objects.get(uid="...")
party.generate_video(width=1080, height=1080, duration=10)

Die Video-URL wird automatisch aktualisiert, wenn das Rendering abgeschlossen ist:

1
2
3
4
5
{% if party.video_url %}
<video controls>
  <source src="{{ party.video_full_url }}" type="video/webm">
</video>
{% endif %}

Nachste Schritte

Wir erkunden mehrere Verbesserungen:

  • Echtzeit-Vorschau - Live-Rendering, wahrend Benutzer Routen zeichnen
  • Mehrere Kamerawinkel - Seitenansichten, Luftaufnahmen
  • Wetter-Overlay - Zeigen Sie vorhergesagte Bedingungen entlang der Route
  • Hohenprofil - Visualisieren Sie Anstiege und Abfahrten im Video

Routen-Uberflugvideos sind zu einer unserer beliebtesten Funktionen geworden und helfen Radfahrern, fundierte Entscheidungen daruber zu treffen, an welchen Fahrten sie teilnehmen mochten. Die Kombination moderner Web-Technologien macht das, was eine teure Produktionsaufgabe gewesen ware, zu einem automatisierten On-Demand-Service.


Mochten Sie es ausprobieren? Erstellen Sie eine Party und Ihr Routenvideo wird automatisch generiert!