Streamen von mp4 in Chrome mit Rails, nginx und send_file

8

Ich kann nicht für das Leben von mir einen mp4 nach Chrome mit einem html5 <video> -Tag streamen. Wenn ich die Datei in public ablege, dann ist alles Soße und funktioniert wie erwartet. Aber wenn ich versuche, es mit send_file zu bedienen, läuft so ziemlich alles Mögliche schief. Ich verwende eine Rails-App, die von nginx proxied ist, mit einem Video -Modell, das ein location -Attribut hat, das ein absoluter Pfad auf der Festplatte ist.

Zuerst habe ich versucht:

%Vor%

Und ich war mir sicher, dass ich mich in dem Glanz der modernen Webentwicklung sonnen würde. Ha. Dies spielt sowohl in Chrome als auch in Firefox, aber keiner sucht und hat auch keine Ahnung, wie lange das Video ist. Ich stocherte in den Antwortheadern und stellte fest, dass Content-Type als application/octet-stream gesendet wurde und dass Content-Length nicht gesetzt war. Umm ... wth?

Okay, ich denke, ich kann diese in Schienen setzen:

%Vor%

An diesem Punkt funktioniert alles so wie erwartet in Firefox. Es weiß, wie lange das Video läuft und die Suche funktioniert wie erwartet. Chrome scheint zu wissen, wie lange das Video ist (zeigt keine Zeitstempel, aber die Suchleiste scheint angemessen), aber die Suche funktioniert nicht.

Anscheinend ist Chrome wählerischer als Firefox. Dazu muss der Server mit einem Accept-Ranges -Header mit dem Wert bytes antworten und auf nachfolgende Anfragen (die bei Suchanfragen auftreten) mit 206 und dem entsprechenden Teil der Datei antworten.

Okay, also habe ich etwas Code von hier ausgeliehen und dann hatte ich folgendes:

%Vor%

Jetzt versucht Chrome zu suchen, aber wenn Sie das Video nicht mehr wiedergeben, funktioniert es nie wieder, bis die Seite neu geladen wird. Argh. Also habe ich beschlossen, mit Curl herumzuspielen, um zu sehen, was passiert ist und ich habe das entdeckt:

  

$ curl --header "Bereich: Bytes = 200-400" Ссылка    ftypisomisomiso2avc1mp41 moovlmvhd @ trak \ tkh

     

$ curl --header "Bereich: Bytes = 1200-1400" Ссылка    ftypisomisomiso2avc1mp41 moovlmvhd @ trak \ tkh

Unabhängig von der Bytebereichsanforderung beginnen die Daten immer am Anfang der Datei. Die entsprechende Anzahl an Bytes wird zurückgegeben (in diesem Fall 201 Byte), aber sie befindet sich immer am Anfang der Datei. Scheinbar respektiert nginx den Header Content-Length , ignoriert aber den Header Content-Range .

Mein nginx.conf ist unberührt Standard:

%Vor%

und meine app.conf ist ziemlich einfach:

%Vor%

Zuerst habe ich das nginx 1.4.x, das mit Ubuntu 14.04 kommt, ausprobiert, dann habe ich 1.7.x aus einem ppa ausprobiert - gleiche Ergebnisse. Ich habe sogar apache2 ausprobiert und hatte genau die gleichen Ergebnisse.

Ich möchte noch einmal darauf hinweisen, dass die Videodatei nicht das Problem ist. Wenn ich es in public ablege, bedient nginx es mit den passenden Mime-Typen, Kopfzeilen und allem, was benötigt wird, damit Chrome richtig funktioniert.

Also meine Frage ist ein Zweiteiler:

  • Warum behandelt nginx / apache das ganze Zeug nicht automatisch mit send_file ( X-Accel-Redirect / X-Sendfile ), wie wenn die Datei statisch von public geliefert wird? Die Handhabung dieses Materials in Schienen ist so rückwärts.

  • Wie zum Teufel kann ich tatsächlich send_file mit nginx (oder Apache) verwenden, so dass Chrome glücklich sein wird und Suche zulassen?

  

Aktualisieren 1

Okay, also dachte ich, ich würde versuchen, die Komplikation der Schienen aus dem Bild zu entfernen und nur zu sehen, ob ich Nginx bekommen könnte, um die Datei korrekt zu übertragen. Also habe ich einen todschicken nodjs-Server hochgespielt:

%Vor%

Und Chrom ist glücklich wie eine Muschel. = / curl -I zeigt sogar an, dass Accept-Ranges: bytes und Content-Type: video/mp4 automatisch von nginx eingefügt werden - wie es sollte sein sollte. Was könnten Schienen tun, die Nginx davon abhalten, dies zu tun?

  

Update 2

Ich könnte näher kommen ...

Wenn ich:

%Vor%

Dann bekomme ich:

%Vor%

Und ich habe alle oben beschriebenen Probleme.

Aber wenn ich:

%Vor%

Dann bekomme ich:

%Vor%

Und alles funktioniert perfekt.

Aber warum? Diese sollten genau das Gleiche tun. Und warum setzt nginx Content-Type nicht automatisch wie für das einfache nodejs Beispiel? Ich habe config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' gesetzt. Ich habe es zwischen application.rb und development.rb mit den gleichen Ergebnissen hin und her verschoben. Ich denke, ich habe nie erwähnt ... das ist Schienen 4.2.0.

  

Update 3

Nun habe ich meinen Unicorn-Server gewechselt, um Port 3000 zu hören (da ich nginx bereits geändert habe, um 3000 für das nodejs-Beispiel zu hören). Jetzt kann ich Anfragen direkt an ein Einhorn stellen (da es an einem Port und nicht an einem Socket lauscht), also habe ich festgestellt, dass curl -I direkt an Einhorn zeigt, dass kein X-Accel-Redirect Header gesendet wird und nur curl ingicorn direkt sendet die Datei. Es ist wie send_file tut nicht, was es soll.

    
ihaztehcodez 18.01.2015, 10:01
quelle

1 Antwort

5

Ich endlich habe die Antworten auf meine ursprünglichen Fragen. Ich hätte nicht gedacht, dass ich jemals hier ankommen würde. All meine Recherchen hatten zu Sackgassen, Hacky-Non-Lösungen und "es funktioniert einfach aus der Box" (na ja, nicht für mich).

  

Warum behandelt nginx / apache das ganze nicht automatisch mit send_file (X-Accel-Redirect / X-Sendfile), als wenn die Datei statisch von public geliefert wird? Die Handhabung dieses Materials in Schienen ist so rückwärts.

Sie tun es, aber sie müssen richtig konfiguriert werden, um Rack :: Sendfile (siehe unten) zu bitten. Der Versuch, dies in Schienen zu handhaben, ist eine hacky non-solution.

  

Wie zum Teufel kann ich tatsächlich send_file mit nginx (oder Apache) verwenden, so dass Chrome glücklich sein wird und Suche zulassen?

Ich war verzweifelt genug, um den Quellcode des Racks herumzustöbern, und dort fand ich meine Antwort in den Kommentaren von Rack::Sendfile . Sie sind als Dokumentation strukturiert, die Sie unter rubydoc finden.

Aus irgendeinem Grund erfordert Rack::Sendfile , dass der Front-End-Proxy einen Header X-Sendfile-Type sendet. Im Fall von nginx benötigt es auch einen Header X-Accel-Mapping . Die Dokumentation enthält auch Beispiele für Apache und Lighttpd.

Man könnte meinen, dass die Rails-Dokumentation mit der Rack :: Sendfile-Dokumentation verlinkt sein könnte, da send_file ohne zusätzliche Konfiguration nicht sofort einsatzbereit ist. Vielleicht werde ich eine Pull-Anfrage einreichen.

Am Ende musste ich nur ein paar Zeilen zu meiner app.conf hinzufügen:

%Vor%

Jetzt funktioniert mein ursprünglicher Code wie erwartet:

%Vor%

Bearbeiten:

Obwohl dies anfänglich funktionierte, hörte es auf zu funktionieren, nachdem ich meine Landstreicherbox neu gestartet hatte und ich weitere Änderungen vornehmen musste:

%Vor%

Ich finde, dass die ganze Sache einen URI einem anderen zuordnet und dann diesen URI auf einen Ort auf dem Datenträger abbildet, um völlig unnötig zu sein. Es ist nutzlos für meinen Anwendungsfall und ich kartiere nur einen zum anderen und wieder zurück. Apache und Lighttpd benötigen es nicht. Aber zumindest funktioniert es.

Ich habe auch Mime::Type.register('video/mp4', :mp4) zu config/initializers/mime_types.rb hinzugefügt, damit die Datei mit dem richtigen MIME-Typ geliefert wird.

    
ihaztehcodez 18.01.2015, 14:22
quelle