2013-10-24 7 views
11

Ich habe eine Anwendung, die Videos aus unserer lokalen Datenbank streamen wollte. Ich habe gestern viel Zeit damit verbracht, zu versuchen, die Daten entweder RangeFileContentResult oder RangeFileStreamResult ohne Erfolg zurückgeben.RangeFileContentResult und Video-Streaming mit Fernanfragen

Kurz gesagt, wenn ich die Datei als eines dieser beiden Ergebnisse zurückgebe, kann ich nicht scheinen, ein Video zu erhalten, richtig zu streamen (oder überhaupt zu spielen).

Range: bytes=0- 

Und die Antwort zur Verfügung gestellt kommt gibt diese Header als Beispiel:

Accept-Ranges: bytes 
Content-Range: bytes 0-5103295/5103296 

In Bezug auf den Netzwerkverkehr, bekomme ich

Die Anfrage vom Browser wird mit den folgenden Header gesendet eine Reihe von 206 für Teilergebnisse, dann eine 200 am Ende (nach Fiddler), die richtig erscheint. Die Netzwerk-Registerkarte von Chrome stimmt dem nicht zu und es gibt eine erste Anfrage (immer 13 Bytes, von denen ich annehme, dass es sich um einen Handshake handelt) und dann noch ein paar Anfragen, die entweder abgebrochen oder ausstehend sind. Soweit ich verstehe, ist das mehr oder weniger korrekt, 206 - Abbrechen, 206 - Abbrechen etc. Aber das Video wird nie abgespielt.

Wenn ich das Ergebnis von meinem Controller auf ein FileResult umschalte, wird das Video abgespielt und Chrome, IE10 und Firefox und scheint zu spielen, bevor das Ende des Downloads abgeschlossen ist (das fühlt sich ein wenig an wie es streamt! es ist nicht)

Aber mit der Reichweite Ergebnis bekomme ich nichts in Chrom oder IE und das gesamte Video-Downloads in einem Fall in Firefox.

Soweit ich verstanden habe, sollte die RangeFileContentResult Antwort auf den Client mit einer Reihe von Bytes zum Download (was mir nicht scheint zu tun, es sagt nur, um die gesamte Datei (durch die obige Antwort dargestellt))). Und der Klient sollte darauf antworten, was er nicht zu tun scheint.

Hat jemand irgendwelche Gedanken in diesem Bereich? Speziell:

a) Sollte RangeFileContentResult eine Reihe von Bytes zurück an den Client senden? b) Gibt es eine Möglichkeit, den Umfang der Bytes, die vom Client angefordert werden, explizit zu steuern? c) Gibt es irgendeinen Grund oder etwas, was ich hier falsch mache, was dazu führen würde, dass Browser das Video überhaupt nicht laden würden, wenn Sie eine RangeFileContentResult anfordern?

EDIT: Hinzugefügt ein Diagramm, um zu beschreiben, was ich sehe:

RangedRequestImage

EDIT2: Ok, also die Handlung verdichtet. Während des Spiel mit dem RangedFile gubbins brauchten wir eine andere System Testversion heraus zu drücken und ich verließ den ‚RangeFileContentResult‘ auf meiner Controller-Aktion wie folgt:

private ActionResult RetrieveVideo(MediaItem media) 
    { 
     return new RangeFileContentResult(media.Content, media.MimeType, media.Id.ToString(), DateTime.Now);    
    } 

Eher seltsam, jetzt scheint dies auf unseren Azure wie erwartet zu funktionieren Systemtestumgebung, aber immer noch nicht auf meinem lokalen Rechner. Ich frage mich, ob es etwas IIS basiert, das glücklich auf Azure IIS8 funktioniert, aber nicht auf meiner lokalen 7.5-Instanz?

+0

Hallo. Ich bin der Lib.Web.Mvc Autor und ich werde mich freuen, in dieser speziellen Ausgabe zu graben. Wir hatten eine ähnliche mit Safari auf dem iPad, die dadurch verursacht wurde, dass sie die Anfrage ohne Obergrenze machte, nur um die Information über die Datei zu bekommen. Der Browser löschte dann die Anfrage, ohne auf die Übertragung der Daten zu warten (im Allgemeinen wurde das Protokoll vom Browser verletzt). Dies könnte etwas Ähnliches sein. Bitte senden Sie mir eine E-Mail mit Details zu Ihrer Umgebung, damit ich das Problem erneut erstellen kann. Sie können auch den gesamten Quelltext von Lib.Web.Mvc und einige andere Dinge hier finden: http: \ tpeczek.codeplex.com. – tpeczek

+0

@tpeczek Hallo Thomasz, danke dafür, kannst du mir die beste Adresse mitteilen, mit der ich dich kontaktieren kann. Die merkwürdigste Sache, die ich sehe, ist die Inkonsistenz zwischen dem Laden vom selben Build. z.B. Für Benutzer 1 funktioniert es, für Benutzer 2 nicht. Dann wird Benutzer 1 aktualisiert und es funktioniert, aber Benutzer 2 wird aktualisiert und nicht. Lass mich die beste Adresse wissen und ich werde dir ein paar Details geben. – dougajmcdonald

+0

Der aus meinem Profil: [email protected] Bitte geben Sie so viele Details wie möglich an (Code, Video, beteiligte Browser, Hosting-Konfiguration, Ausnahmen, Logs etc.) – tpeczek

Antwort

4

Der Grund des Problems hier beschrieben wird, ist der Wert auf modificationDate Parameter von RangeFileContentResult Konstruktor übergeben:

return new RangeFileContentResult(media.Content, media.MimeType, media.Id.ToString(), DateTime.Now); 

Dieses Datum der RangeFileResult verwendet wird, um zwei Header zu erstellen:

  • ETag - Dieser Header ist eine Kennung, die von Browser und Server verwendet wird, um sicherzustellen, dass sie über dieselbe Entität sprechen.
  • - Dieser Header informiert den Browser über das Datum der letzten Änderung der Entität.

Die Tatsache, dass ein DateTime.Now wird der Browser macht Teilanforderung ein Grund Wert für ETag und Last-Modified Header sein könnte jedes Mal übergeben werden ändern, bevor der Client die gesamte Einheit, wenn bekommen (in der Regel der gesamte Prozess dauert länger als eine Sekunde).

Im oben beschriebenen Fall sendet der Browser If-Range Header mit der Anfrage. Dieser Header teilt dem Server mit, dass die gesamte Entität erneut gesendet werden sollte, wenn das Entitäts-Tag (oder Änderungsdatum, da If-Range einen dieser beiden Werte enthalten kann) nicht viel enthält. Dies geschieht in diesem Fall.

Die Tatsache, dass das Änderungsdatum „dynamic“ verursachen können auch weitere Probleme, wenn eine der folgenden Client-Header für die Verifikation zu verwenden entscheidet: If-Modified-Since, If-Unmodified-Since, If-Match, If-None-Match.

Die Lösung in dieser Situation ist, ein Änderungsdatum in der Datenbank mit der Datei zu halten, um sicherzustellen, dass es konsistent ist.

Hier gibt es auch einen Platz zur Optimierung. Anstatt jedes Mal, wenn eine Teilanforderung gestellt wird, das gesamte Video aus der DB zu holen, kann man es entweder zwischenspeichern oder nur den relevanten Teil erfassen (wenn die Datenbankmaschine, die eine Anwendung verwendet, eine solche Operation zulässt). Solch ein Mechanismus kann verwendet werden, um ein spezielles Aktionsergebnis zu erzeugen, indem von RangeFileResult und WriteEntireEntity und WriteEntityRange Übermittlungsmethoden geliefert werden.

+0

Dies ist die richtige Antwort, ich habe eine DateTime.Now als das modifizierte Datum verwendet, da ich die Wichtigkeit des Parameters nicht verstanden habe. Als eine Nebensache könnte eine Dokumentation zu diesem Parameter nützlich sein, als würden Sie keine statischen Dateien streamen (wie wir es nicht waren), ist es möglich, dass Sie für jede Datei kein "Änderungsdatum" speichern. – dougajmcdonald

+0

@dougajmcdonald Wie Sie vorgeschlagen haben, wird die Dokumentation erweitert – tpeczek

+0

Hallo Thomasz, Nochmals vielen Dank für Ihre Hilfe beim Sortieren dieses, wirklich zu schätzen, die Mühe genommen. – dougajmcdonald

3

mofiPlease kopieren Sie einfach diese beiden Dateien in Ihrem MVC-Projekt
RangeFileResult
RangeFileStreamResult

public ActionResult Movie() 
{ 
    var path = new FileStream(@"C:\temp\01.avi", FileMode.Open); 
    return new RangeFileStreamResult(path, "video/x-msvideo", "01.avi", DateTime.Now); 
} 

Jetzt Ihr Projekt und offen in Chrom (zum Beispiel: http://youraddress.com:45454/Main/Movie) laufen sollten Sie Ihre Datei, spielen einen Standard verwenden Chrom-Videoplayer. es ist das Streaming und man kann es sehen, wenn Sie bei

return new RangeFileStreamResult(path, "video/x-msvideo", "01.avi", DateTime.Now); 

Wieder ist die Quelle einen Haltepunkt setzen ist einfach, die Puffergröße zu ändern, zu ändern, die für das Streaming verwendet wird!

+0

Hallo Anton, danke für die Antwort. Eine kurze Frage, bevor ich den Code ein wenig überarbeite. Unsere Videos sind in der db gespeichert und ich gebe eine Entity mit media.Content als ein Byte [] und media.MimeType als String zurück, ich habe diese Elemente als Parameter auf ein 'RangeFileContentResult' aufgefüllt, das diese Werte vermutlich akzeptiert das sollte auch funktionieren? Oder müssen Sie speziell einen Dateistream öffnen und das RangeFileStreamResult verwenden? – dougajmcdonald

3

Ok Also habe ich nicht genug Zeit, um RangeFileResult in Details zu sehen, aber ich habe einfach die Datei (RangeFileContentResult) von RangeFileContentResult

und meinen Code so modifiziert, dass es wie

public ActionResult Movie() 
{ 

    byte[] file = System.IO.File.ReadAllBytes(@"C:\HOME\asp\Java\Java EE. Programming Spring 3.0\01.avi"); 

    return new RangeFileContentResult(file, "video/x-msvideo", "01.avi", DateTime.Now); 

} 
sieht

und wieder funktioniert es. Ich bemerkte jedoch, dass, wenn ich das Video stoppen ich eine Ausnahme haben und es geschieht in RangeFileResult

if (context.HttpContext.Response.IsClientConnected) 
{ 
    WriteEntityRange(context.HttpContext.Response, RangesStartIndexes[i], RangesEndIndexes[i]); 
    if (MultipartRequest) 
       context.HttpContext.Response.Write("\r\n"); 
    context.HttpContext.Response.Flush(); 
} 

Also besser Sie den Code ändern, um it.In Bedingungen zu behandeln, wenn Benutzer bereits getrennt, aber sie sind immer noch versuchen zu senden sie eine Antwort.

Wieder ist es technisch kein großer Unterschied, ob Sie byte [] oder Stream übergeben, da selbst dann, wenn Sie den Code mit ihm arbeiten Streamen passieren

using (FileStream) 
      { 
       FileStream.Seek(rangeStartIndex, SeekOrigin.Begin); 

       int bytesRemaining = Convert.ToInt32(rangeEndIndex - rangeStartIndex) + 1; 
       byte[] buffer = new byte[_bufferSize]; 

       while (bytesRemaining > 0) 
       { 
        int bytesRead = FileStream.Read(buffer, 0, _bufferSize < bytesRemaining ? _bufferSize : bytesRemaining); 
        response.OutputStream.Write(buffer, 0, bytesRead); 
        bytesRemaining -= bytesRead; 
       } 
      } 

wieder Daten liest und setzt sie in ein byte [] Array! .... Es liegt also an dir!

ABER ... Ich schlage vor, dass Sie achten auf einen Inhaltstyp, den Sie bereitstellen !!! Punkt ist, dass Ihr Browser Lage sein muss, sie zu handhaben! Also, wenn Sie etwas bieten unbekannt definitiv haben problems.To Sie Ihren Inhaltstyp String finden Sie bitte auf mime-types-by-content-type

Wieder gab ich nur einen kurzen Blick und wenn Sie Probleme Ich werde dir später helfen, wenn du nach Hause kommst.

+0

Danke nochmal Anton, ich werde das später mit einem feinen Zahnkamm durchgehen und euch das Ergebnis wissen lassen. Als ein paar Nebenpunkte, ich habe die neueste Lib.Web.MVC von Nuget, so hoffe ich, dass deckt die Notwendigkeit für die Dateien (obwohl ich den Inhalt überprüfen werde, um sicherzustellen, dass ich nicht habe alte Kopie). Außerdem werden meine Inhaltstypen gespeichert, wenn das Video mithilfe des Inhaltstyps aus der HttpPostedFileBase hochgeladen wird. Ich akzeptiere, dass nicht alle Browser mit allen Erweiterungen umgehen, aber mit dem Testvideo, das ich benutze, wenn ich ein einfaches FileResult zurück gebe, rendert es korrekt, aber nur in einem Stück ohne Suche. – dougajmcdonald

Verwandte Themen