HTML5-Video wird nicht wiederholt

73

Ich habe ein Video als Hintergrund für eine Webseite und versuche, es in eine Schleife zu bringen. Hier ist der Code:

<video autoplay='true' loop='true' muted='true'>
  <source src='/admin/wallpapers/linked/4ebc66e899727777b400003c' type='video/mp4'></source>
</video>

Obwohl ich das Video angewiesen habe, eine Schleife zu erstellen, ist dies nicht der Fall. Ich habe auch versucht, eine Schleife mit dem onendedAttribut zu erstellen (gemäß diesem Mozilla-Support-Thread habe ich auch dieses bisschen jQuery ausprobiert). Bisher hat nichts funktioniert. Ist es ein Problem mit Chrome oder meinem Code?

Bearbeiten:

Ich habe die Netzwerkereignisse und den KOPF einer Arbeitskopie ( http://fhsclock-labs.heroku.com/no-violence ) im Vergleich zu der Anwendung überprüft, mit der ich arbeiten möchte . Der Unterschied besteht darin, dass die Arbeitskopie das Video von einem statischen Asset auf Heroku (anscheinend über Varnish) bereitstellt, während meine von GridFS (MongoDB) bereitgestellt wird.

Auf der Registerkarte "Netzwerk" des Chrome-Inspektors wird angezeigt, dass das Video in meiner Anwendung dreimal angefordert wird. Einmal ist der Status "ausstehend", der zweite ist "abgebrochen" und der letzte ist 200 OK. Die Arbeitskopie zeigt nur zwei Anfragen, der eine Status steht noch aus und der andere ist Teilinhalt. Nachdem das Video einmal abgespielt wurde, ändert sich diese Anforderung in "Abgebrochen" und es wird eine weitere Anforderung für dieses Video gestellt. In meiner Bewerbung passiert das nicht.

In meiner Anwendung sind zwei "undefiniert" und das andere "video / mp4" (was es sein soll). In der funktionierenden App sind alle Anfragen "video / mp4".

Außerdem erhalte ich Resource interpreted as Other but transferred with MIME type undefined.Warnungen in der Konsole.

Ich bin mir nicht ganz sicher, wo ich damit anfangen soll. Ich bin der Meinung, dass das Problem serverseitig ist, da das Bereitstellen der Datei als statische Assets einwandfrei funktioniert. Möglicherweise sendet der Server nicht den richtigen Inhaltstyp. Dies könnte ein Problem mit GridFS sein. Ich weiß es nicht.

In jedem Fall ist die Quelle hier . Jeder Einblick, den Sie anbieten können, wird geschätzt.

Ethan Turkeltaub
quelle

Antworten:

127

Ah, ich bin gerade auf genau dieses Problem gestoßen.

Wie sich herausstellt, funktioniert das Schleifen (oder jede Art von Suchen) in <video>Elementen in Chrome nur, wenn die Videodatei von einem Server bereitgestellt wurde, der teilweise Inhaltsanforderungen versteht . Das heißt, der Server muss Anforderungen berücksichtigen, die einen "Range" -Header mit einer 206-Antwort "Partial Content" enthalten. Dies ist sogar dann der Fall, wenn das Video klein genug ist, um vollständig von Chrome gepuffert zu werden, und keine Server-Roundtrips mehr durchgeführt werden: Wenn Ihr Server die Range-Anforderung von Chrome beim ersten Mal nicht berücksichtigt hat, ist das Video nicht wiederholbar oder durchsuchbar .

Also ja, ein Problem mit GridFS, obwohl Chrome wohl verzeihender sein sollte.

afri
quelle
3
Hatte gerade das gleiche Problem. Ich verwende nginx, also musste ich nginx löschen, mit nginx.org/en/docs/http/ngx_http_mp4_module.html von der Quelle installieren und alles neu starten.
Chriscauley
Das gleiche passiert mit dem in PHP eingebauten Server (in meiner Entwicklungsumgebung).
Marcel Burkhard
3
Gleiches Problem mit Django 1.7.10 Runserver.
Lauri Elias
Ich hatte das gleiche Problem aufgrund einer schlecht konfigurierten Unternehmensfirewall - eine Bereichsanforderung wird entfernt und Sie erhalten die gesamte Datei zurück. Safari weigert sich, es überhaupt zu spielen, Chrome spielt es einmal. Daran kann nichts geändert werden, außer vielleicht zu HLS und einem Fallback-JS-Player zu wechseln, der dies unterstützt. Ich könnte curl -v -r0-1 ...
Fehler beheben
@Marcel Burkhard Hey, kannst du mir den PHP-Code zeigen, mit dem du dieses Problem gelöst hast? Ich habe das gleiche Problem und sehr neu in PHP.
Ng2-Fun
18

Einfachste Problemumgehung:

$('video').on('ended', function () {
  this.load();
  this.play();
});

Das 'ended'Ereignis wird ausgelöst, wenn das Video das Ende erreicht, video.load()das Video auf den Anfang zurückgesetzt und video.play()die Wiedergabe sofort nach dem Laden gestartet.

Dies funktioniert gut mit Amazon S3, wo Sie nicht so viel Kontrolle über Serverantworten haben, und umgeht auch Firefox-Probleme, die damit zusammenhängen, video.currentTimedass ein Video nicht einstellbar ist, wenn seine Metadaten für die Länge fehlen.

Ähnliches Javascript ohne jQuery:

document.getElementsByTagName('video')[0].onended = function () {
  this.load();
  this.play();
};
d2vid
quelle
Gibt es eine Chance, dass Sie das jQuery-freie Äquivalent kennen?
Brian Brownton
Dies funktioniert gut, obwohl das Video beim Neuladen kurz blinkt.
Prideout
6

Es sieht so aus, als wäre es in der Vergangenheit ein Problem gewesen. Es gibt mindestens zwei geschlossene Fehler, aber beide geben an, dass es behoben wurde:

http://code.google.com/p/chromium/issues/detail?id=39683

http://code.google.com/p/chromium/issues/detail?id=18846

Da sowohl Chrome als auch Safari Webkit-basierte Browser verwenden, können Sie möglicherweise einige dieser Problemumgehungen verwenden: http://blog.millermedeiros.com/2011/03/html5-video-issues-on-the-ipad-and-how -zu-lösen-sie /

function restartVideo(){
vid.currentTime = 0.1; //setting to zero breaks iOS 3.2, the value won't update, values smaller than 0.1 was causing bug as well.
vid.play();
}

//loop video
vid.addEventListener('ended', restartVideo, false);
John
quelle
@Ramesh Könntet ihr euch auch diese Frage ansehen? stackoverflow.com/questions/31634678/... Hat mit Videodaten zu tun
committedandroider
3

Meine Situation:

Ich habe genau das gleiche Problem, aber das Ändern des Headers der Antwortnachricht allein hat nicht funktioniert. Keine Schleife, Wiederholung oder Suche. Auch ein reiner Stopp funktioniert nicht, aber das könnte meine Konfiguration sein.

Antworten:

Laut einigen Sites (konnte sie nicht mehr finden) ist es auch möglich, die load () -Methode direkt nach dem Ende des Videos und vor dem Start der nächsten auszulösen. Das sollte die Quelle neu laden und ein wieder funktionierendes Video / Audio-Element verursachen.

@John

Bitte beachten Sie, dass Ihre Antworten / Links normale Fehler sind und sich nicht auf dieses Problem konzentrieren. Die Verwendung eines Servers / Webservers verursacht dieses Problem. Während die Fehler, die diese Links beschreiben, von einer anderen Art sind. Das ist auch der Grund, warum die Antwort nicht funktioniert.

Ich hoffe es hilft, ich suche noch nach einer Lösung.

Wer weiß
quelle
Am Ende habe ich es aus einer anderen Quelle bereitgestellt: bevor ich MongoDBs GridFS verwendet habe. Ich glaube, das Problem ist darauf zurückzuführen, dass die Datei falsch bereitgestellt wurde. Ich verwende Amazon S3 jetzt ohne Probleme.
Ethan Turkeltaub
Ohh .. wir haben es auch gelöst, aber ich kann es nicht wirklich erklären. Es scheint, dass Bereichsanforderungen für den Dienst nicht funktionierten, daher haben wir diese Zeilen zu unserer Methode hinzugefügt, mit der die Dateien heruntergeladen werden. response.Headers.Add ("Accept-Ranges", "Bytes"); response.Headers.Add ("Inhaltsbereich", rangeValue.Replace ("=", "") + (resource.Value.Length - 1) .ToString () + "/" + resource.Value.Length.ToString ( )); response.StatusCode = HttpStatusCode.PartialContent; Dies aktivierte die Bereichsanforderungen und manipulierte den Header, um den Server usw. über eine Bereichsanforderung zu informieren. Code 206 statt 200.
WhoKnows
response.Headers.Add ("Accept-Ranges", "Bytes"); response.Headers.Add ("Inhaltsbereich", rangeValue.Replace ("=", "") + (resource.Value.Length - 1) .ToString () + "/" + resource.Value.Length.ToString ( )); response.StatusCode = HttpStatusCode.PartialContent;
WhoKnows
Es tut mir leid, wenn das etwas unklar ist, aber vielleicht hilft es irgendwann jemandem :) Danke, dass du mich in die richtige Richtung gelenkt hast!
WhoKnows
Vielen Dank! Ich verwende video.js und beim Aufruf von load on end wurde die Video-Schleife in der Produktion ausgeführt, wenn dies zuvor nur lokal der Fall war. Ich habe festgestellt, dass ich überprüfen kann, ob die aktuelle Zeit === Dauer am Ende ist, um festzustellen, ob das Video nicht zum Starten zurückgeschleift wird, und in diesem Fall Laden statt Abspielen aufrufen.
Funrob
3

Nur für den Fall, dass Ihnen keine der oben genannten Antworten hilft, stellen Sie sicher, dass Ihr Inspektor nicht mit aktivierter Option Cache deaktivieren ausgeführt wird. Da Chrome das Video aus dem Cache abruft, funktioniert es grundsätzlich einmal. Debuggen Sie dies nur 20 Minuten lang, bevor Sie feststellen, dass dies die Ursache ist. Als Referenz und so weiß ich, dass ich nicht der einzige Chrom-Bug-Bericht eines anderen bin .

srussking
quelle
1

Es ist super lahm, aber Dropbox verwendet den richtigen Statuscode. Also in die Dropbox hochladen und das www durch dl ersetzen.

Mit einer Dropbox-URL kann das Video also einwandfrei abgespielt werden.

gabrielstuff
quelle
0

Ich hatte das gleiche Problem und löste das Problem unweigerlich durch Streaming des Inhalts.

Beispiel: Dies ist der Code mit PHP-Laravel-Blade-HTML-Code, der eine Streaming-Route anfordert:

<video>
    <source src="{{route('getVideoStream',$videoId)}}" type="video/mp4"/>
</video>

Im Controller streame ich das Video und gebe es als Laravel-Stream-Funktion zurück:

   public function getVideoStream($videoId){

        $path = $pathOfVideo;

        $headers = [
            'Content-Type' => 'video/mp2t',
            'Content-Length' => File::size($path),
            'Content-Disposition' => 'attachment; filename="start.mp4"'
        ];

        $stream = new VideoStream($path);

        return response()->stream(function () use ($stream) {
            $stream->start();
        });
    }

und VideoStream Class ist die Streaming-Klasse, die ich aus einem GitHub- Gist gefunden habe :

class VideoStream
{
    private $path = "";
    private $stream = "";
    private $buffer = 102400;
    private $start = -1;
    private $end = -1;
    private $size = 0;

    function __construct($filePath)
    {
        $this->path = $filePath;
    }

    /**
     * Open stream
     */
    private function open()
    {
        if (!($this->stream = fopen($this->path, 'rb'))) {
            die('Could not open stream for reading');
        }

    }

    /**
     * Set proper header to serve the video content
     */
    private function setHeader()
    {
        ob_get_clean();
        header("Content-Type: video/mp4");
        header("Cache-Control: max-age=2592000, public");
        header("Expires: " . gmdate('D, d M Y H:i:s', time() + 2592000) . ' GMT');
        header("Last-Modified: " . gmdate('D, d M Y H:i:s', @filemtime($this->path)) . ' GMT');
        $this->start = 0;
        $this->size = filesize($this->path);
        $this->end = $this->size - 1;
        header("Accept-Ranges: 0-" . $this->end);

        if (isset($_SERVER['HTTP_RANGE'])) {

            $c_start = $this->start;
            $c_end = $this->end;

            list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);
            if (strpos($range, ',') !== false) {
                header('HTTP/1.1 416 Requested Range Not Satisfiable');
                header("Content-Range: bytes $this->start-$this->end/$this->size");
                exit;
            }
            if ($range == '-') {
                $c_start = $this->size - substr($range, 1);
            } else {
                $range = explode('-', $range);
                $c_start = $range[0];

                $c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $c_end;
            }
            $c_end = ($c_end > $this->end) ? $this->end : $c_end;
            if ($c_start > $c_end || $c_start > $this->size - 1 || $c_end >= $this->size) {
                header('HTTP/1.1 416 Requested Range Not Satisfiable');
                header("Content-Range: bytes $this->start-$this->end/$this->size");
                exit;
            }
            $this->start = $c_start;
            $this->end = $c_end;
            $length = $this->end - $this->start + 1;
            fseek($this->stream, $this->start);
            header('HTTP/1.1 206 Partial Content');
            header("Content-Length: " . $length);
            header("Content-Range: bytes $this->start-$this->end/" . $this->size);
        } else {
            header("Content-Length: " . $this->size);
        }

    }

    /**
     * close curretly opened stream
     */
    private function end()
    {
        fclose($this->stream);
        exit;
    }

    /**
     * perform the streaming of calculated range
     */
    private function stream()
    {
        $i = $this->start;
        set_time_limit(0);
        while (!feof($this->stream) && $i <= $this->end) {
            $bytesToRead = $this->buffer;
            if (($i + $bytesToRead) > $this->end) {
                $bytesToRead = $this->end - $i + 1;
            }
            $data = fread($this->stream, $bytesToRead);
            echo $data;
            flush();
            $i += $bytesToRead;
        }
    }

    /**
     * Start streaming video content
     */
    function start()
    {
        $this->open();
        $this->setHeader();
        $this->stream();
        $this->end();
    }
}
Mahdi Taleghani
quelle
-1

Ich weiß, dass dies nicht genau die gestellte Frage betrifft, aber wenn jemand bei einem ähnlichen Problem darauf stößt, stellen Sie sicher, dass Ihre Quellen ordnungsgemäß in Ordnung sind.

Ich habe eine mp4und eine webmDatei geladen und festgestellt, dass das Video in Chrome nicht geloopt wurde. Dies lag daran, dass die webmDatei als erste sourceaufgeführt war, sodass Chrome die webmDatei und nicht die Datei lud mp4.

Hoffe, das hilft jemand anderem, der auf dieses Problem stößt.

<video autoplay loop>
    <source src="/path-to-vid/video.mp4" type="video/mp4">
    <source src="/path-to-vid/video.webm" type="video/webm">
</video>
ferne97
quelle