Hier erfahren Sie, wie Sie die Medienwiedergabe durch aktives Vorabladen von Ressourcen beschleunigen.
<ph type="x-smartling-placeholder">
Ein schnellerer Start der Wiedergabe bedeutet, dass sich mehr Nutzer Ihr Video ansehen oder sich Ihre Musik anhören Audio. Das ist bekannt. In diesem Artikel geht es um mit denen Sie die Audio- und Videowiedergabe beschleunigen können, je nach Anwendungsfall Ressourcen vorab laden.
Ich werde drei Methoden zum Vorabladen von Mediendateien beschreiben, angefangen mit den Profis. und Nachteile.
Das ist großartig... | Aber... | |
---|---|---|
Attribut „Video-Preload“ | Einfache Verwendung für eine eindeutige Datei, die auf einem Webserver gehostet wird. | Browser ignorieren das Attribut möglicherweise vollständig. |
Der Ressourcenabruf beginnt, wenn das HTML-Dokument vollständig geladen wurde und geparst. | ||
Media Source Extensions (MSE) ignorieren das preload -Attribut für Medienelemente, da die App für
der Medienbereitstellung für MSE.
|
||
Link vorab laden |
Erzwingt, dass der Browser eine Videoressource anfordert, ohne zu blockieren
Das onload -Ereignis des Dokuments.
|
HTTP-Bereichsanfragen sind nicht kompatibel. |
Kompatibel mit MSE- und Dateisegmenten. | Sollte nur für kleine Mediendateien (< 5 MB) verwendet werden, wenn vollständige Ressourcen abgerufen werden. | |
Manuelle Zwischenspeicherung | Uneingeschränkter Zugriff | Die Website ist für die Behebung komplexer Fehler verantwortlich. |
Attribut für das Vorabladen des Videos
Handelt es sich bei der Videoquelle um eine eindeutige Datei, die auf einem Webserver gehostet wird, empfiehlt es sich,
Verwende das preload
-Attribut des Videos, um dem Browser einen Hinweis darauf zu geben, wie
viele Informationen oder Inhalte vorab laden. Das bedeutet, dass Erweiterungen für Medienquellen
(MSE) ist nicht mit preload
kompatibel.
Das Abrufen der Ressourcen wird erst gestartet, wenn das ursprüngliche HTML-Dokument zuvor
vollständig geladen und geparst (z.B. das Ereignis DOMContentLoaded
ausgelöst wurde)
während das sehr unterschiedliche load
-Ereignis
ausgelöst wird, wenn die Ressource
abgerufen wurde.
Wenn Sie das Attribut preload
auf metadata
festlegen, ist der Nutzer nicht
erwartet, dass sie das Video benötigen, aber dass die Metadaten (Dimensionen, Tracks
Liste, Dauer usw.) wünschenswert ist. Hinweis: Ab Chrome
64 eingeben, ist der Standardwert für preload
metadata
. (Es war auto
zuvor).
<video id="video" preload="metadata" src="file.mp4" controls></video>
<script>
video.addEventListener('loadedmetadata', function() {
if (video.buffered.length === 0) return;
const bufferedSeconds = video.buffered.end(0) - video.buffered.start(0);
console.log(`${bufferedSeconds} seconds of video are ready to play.`);
});
</script>
Wenn das Attribut preload
auf auto
gesetzt wird, bedeutet das, dass der Browser möglicherweise
dass genügend Daten vorhanden sind, um die Wiedergabe zu beenden.
eine weitere Pufferung aus.
<video id="video" preload="auto" src="file.mp4" controls></video>
<script>
video.addEventListener('loadedmetadata', function() {
if (video.buffered.length === 0) return;
const bufferedSeconds = video.buffered.end(0) - video.buffered.start(0);
console.log(`${bufferedSeconds} seconds of video are ready to play.`);
});
</script>
Es gibt jedoch einige Einschränkungen. Da dies nur ein Hinweis ist, kann es sein, dass der Browser
das Attribut preload
ignorieren. Bei der Erstellung dieses Dokuments
finden Sie hier einige Regeln,
in Chrome angewendet:
- Wenn der Datensparmodus aktiviert ist, erzwingt Chrome den Wert
preload
:none
. - In Android 4.3 erzwingt Chrome den Wert
preload
aufgrund eines Android-none
Fehler. - Bei einer Mobilfunkverbindung (2G, 3G und 4G) erzwingt Chrome den
preload
-Wert,metadata
.
Tipps
Wenn Ihre Website viele Videoressourcen in derselben Domain enthält,
empfehlen, den Wert von preload
auf metadata
festzulegen oder die poster
zu definieren
und legen Sie preload
auf none
fest. So vermeiden Sie es,
maximale Anzahl an HTTP-Verbindungen zur selben Domain (6 gemäß dem
HTTP 1.1-Spezifikation), die sich beim Laden von Ressourcen aufhängen kann. Beachten Sie, dass auch hier
die Seitengeschwindigkeit zu verbessern, wenn Videos nicht zu den Hauptfunktionen gehören.
Link vorab laden
<ph type="x-smartling-placeholder">Wie in anderen Artikeln beschrieben, ist das Vorabladen von Links ein deklarativer Abruf,
können Sie den Browser zwingen, eine Ressource anzufordern,
das load
-Ereignis blockiert wird und während die Seite heruntergeladen wird. Ressourcen
über <link rel="preload">
geladen, werden lokal im Browser gespeichert und werden
inaktiv, bis explizit im DOM, in JavaScript,
oder CSS.
Der Unterschied zum Vorabruf liegt insofern vor, als der Schwerpunkt auf der aktuellen Navigation und ruft Ressourcen mit Priorität basierend auf ihrem Typ (Skript, Stil, Schriftart, Video, Audio usw.). Damit kann der Browser-Cache auf die aktuellen Sitzungen.
Vollständiges Video vorab laden
So laden Sie vorab ein vollständiges Video auf Ihrer Website, JavaScript fordert zum Abrufen von Videoinhalten an und wird als Ressource aus dem Cache gelesen. wurde möglicherweise bereits vom Browser im Cache gespeichert. Wenn bei der Vorabladeanfrage abgeschlossen ist, findet ein regelmäßiger Netzwerkabruf statt.
<link rel="preload" as="video" href="https://cdn.com/small-file.mp4">
<video id="video" controls></video>
<script>
// Later on, after some condition has been met, set video source to the
// preloaded video URL.
video.src = 'https://cdn.com/small-file.mp4';
video.play().then(() => {
// If preloaded video URL was already cached, playback started immediately.
});
</script>
Da die vorab geladene Ressource von einem Videoelement in
Im Beispiel lautet der Wert des as
-Links für den Vorabladen video
. Wenn es sich um eine Audioanzeige handelt
Element enthält, wäre es as="audio"
.
Erstes Segment vorab laden
Das folgende Beispiel zeigt, wie das erste Segment eines Videos mit <link
rel="preload">
vorab geladen und mit Media Source Extensions verwendet wird. Wenn Sie nicht vertraut sind,
mit der MSE JavaScript API erhalten Sie unter MSE-Grundlagen.
Nehmen wir der Einfachheit halber an, dass das gesamte Video in
kleinere Dateien wie file_1.webm
, file_2.webm
, file_3.webm
usw.
<link rel="preload" as="fetch" href="https://cdn.com/file_1.webm">
<video id="video" controls></video>
<script>
const mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', sourceOpen, { once: true });
function sourceOpen() {
URL.revokeObjectURL(video.src);
const sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vp09.00.10.08"');
// If video is preloaded already, fetch will return immediately a response
// from the browser cache (memory cache). Otherwise, it will perform a
// regular network fetch.
fetch('https://cdn.com/file_1.webm')
.then(response => response.arrayBuffer())
.then(data => {
// Append the data into the new sourceBuffer.
sourceBuffer.appendBuffer(data);
// TODO: Fetch file_2.webm when user starts playing video.
})
.catch(error => {
// TODO: Show "Video is not available" message to user.
});
}
</script>
Support
<ph type="x-smartling-placeholder">Ob verschiedene as
-Typen für <link rel=preload>
unterstützt werden, erkennen Sie mit dem
Ausschnitte unten:
function preloadFullVideoSupported() {
const link = document.createElement('link');
link.as = 'video';
return (link.as === 'video');
}
function preloadFirstSegmentSupported() {
const link = document.createElement('link');
link.as = 'fetch';
return (link.as === 'fetch');
}
Manuelle Zwischenspeicherung
Bevor wir uns mit der Cache API und den Service Workern befassen, sehen wir uns
Videos mit MSE manuell zwischengespeichert. Im Beispiel unten wird davon ausgegangen,
Server unterstützt HTTP Range
aber das wäre ähnlich wie bei
Segmente. Beachten Sie, dass einige Middleware-Bibliotheken wie die Shaka-Bibliothek von Google
Player, JW Player und Video.js sind
die dies für Sie übernimmt.
<video id="video" controls></video>
<script>
const mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', sourceOpen, { once: true });
function sourceOpen() {
URL.revokeObjectURL(video.src);
const sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vp09.00.10.08"');
// Fetch beginning of the video by setting the Range HTTP request header.
fetch('file.webm', { headers: { range: 'bytes=0-567139' } })
.then(response => response.arrayBuffer())
.then(data => {
sourceBuffer.appendBuffer(data);
sourceBuffer.addEventListener('updateend', updateEnd, { once: true });
});
}
function updateEnd() {
// Video is now ready to play!
const bufferedSeconds = video.buffered.end(0) - video.buffered.start(0);
console.log(`${bufferedSeconds} seconds of video are ready to play.`);
// Fetch the next segment of video when user starts playing the video.
video.addEventListener('playing', fetchNextSegment, { once: true });
}
function fetchNextSegment() {
fetch('file.webm', { headers: { range: 'bytes=567140-1196488' } })
.then(response => response.arrayBuffer())
.then(data => {
const sourceBuffer = mediaSource.sourceBuffers[0];
sourceBuffer.appendBuffer(data);
// TODO: Fetch further segment and append it.
});
}
</script>
Hinweise
Da Sie jetzt die Kontrolle über die Zwischenspeicherung der Medien haben, empfehle ich Ihnen, den Akkuladestand des Geräts, den „Datensparmodus“ die Nutzereinstellungen und Netzwerkinformationen beim Vorabladen.
Akkuerkennung
Berücksichtige den Akkuladestand der Nutzer Geräte, bevor Sie darüber nachdenken, zum Vorabladen eines Videos. Dadurch wird die Akkulaufzeit verlängert, ist niedrig.
Deaktivieren Sie das Vorabladen oder laden Sie zumindest ein Video mit niedrigerer Auflösung vorab, wenn das der Akku Ihres Geräts leer ist.
if ('getBattery' in navigator) {
navigator.getBattery()
.then(battery => {
// If battery is charging or battery level is high enough
if (battery.charging || battery.level > 0.15) {
// TODO: Preload the first segment of a video.
}
});
}
„Datensparmodus“ erkennen
Verwende den Save-Data
-Anfrageheader für Clienthinweise, um schnelle und leichte Übermittlungen zu ermöglichen
Anwendungen für Nutzer*innen, die „Dateneinsparungen“ aktiviert haben in ihrem
Browser. Durch die Identifizierung dieses Anfrage-Headers kann Ihre Anwendung
Nutzererfahrung bei Kosten- und Leistungsbegrenzungen optimieren
Nutzenden.
Weitere Informationen finden Sie unter Delivering Fast and Light Applications with Save-Data.
Intelligentes Laden basierend auf Netzwerkinformationen
Wir empfehlen dir, navigator.connection.type
vor dem Vorabladen zu prüfen. Wann?
auf cellular
gesetzt ist, könnten Sie das Vorabladen verhindern und Nutzer darüber informieren,
berechnet ihr Mobilfunkanbieter möglicherweise die Bandbreite
Automatische Wiedergabe von zuvor im Cache gespeicherten Inhalten
if ('connection' in navigator) {
if (navigator.connection.type == 'cellular') {
// TODO: Prompt user before preloading video
} else {
// TODO: Preload the first segment of a video.
}
}
Im Beispiel mit den Netzwerkinformationen erfährst du, wie du auf ein Netzwerk reagieren kannst. ändert sich auch.
Mehrere erste Segmente vorab im Cache speichern
Was ist, wenn ich Medieninhalte
spekulativ vorab laden möchte,
zu wissen, für welche Medien
der Nutzer sich letztendlich entscheiden wird? Wenn der Nutzer eine
mit 10 Videos angezeigt wird, haben wir wahrscheinlich genug Speicher,
für jede Segmentdatei, aber wir sollten auf keinen Fall 10 versteckte <video>
erstellen.
-Elementen und 10 MediaSource
-Objekten und fangen an, diese Daten einzuspeisen.
Das zweiteilige Beispiel unten zeigt, wie Sie mehrere erste Segmente
können Sie mithilfe der leistungsstarken und nutzerfreundlichen Cache API Videos ansehen. Etwas Ähnliches
kann auch mit IndexedDB erreicht werden. Wir verwenden noch keine Service Worker,
Die Cache API ist auch über das Objekt window
zugänglich.
Abrufen und zwischenspeichern
const videoFileUrls = [
'bat_video_file_1.webm',
'cow_video_file_1.webm',
'dog_video_file_1.webm',
'fox_video_file_1.webm',
];
// Let's create a video pre-cache and store all first segments of videos inside.
window.caches.open('video-pre-cache')
.then(cache => Promise.all(videoFileUrls.map(videoFileUrl => fetchAndCache(videoFileUrl, cache))));
function fetchAndCache(videoFileUrl, cache) {
// Check first if video is in the cache.
return cache.match(videoFileUrl)
.then(cacheResponse => {
// Let's return cached response if video is already in the cache.
if (cacheResponse) {
return cacheResponse;
}
// Otherwise, fetch the video from the network.
return fetch(videoFileUrl)
.then(networkResponse => {
// Add the response to the cache and return network response in parallel.
cache.put(videoFileUrl, networkResponse.clone());
return networkResponse;
});
});
}
Hinweis: Wenn ich HTTP-Range
-Anfragen verwenden würde, müsste ich das Programm manuell neu erstellen.
Ein Response
-Objekt, da die Cache API noch keine Range
-Antworten unterstützt. Seien
Beachten Sie, dass durch das Aufrufen von networkResponse.arrayBuffer()
der gesamte Inhalt
der Antwort gleichzeitig im Arbeitsspeicher des Renderers zu speichern. Daher bietet es sich an,
und kleine Bereiche.
Ich habe einen Teil des obigen Beispiels geändert, um den HTTP-Bereich zu speichern. an den Pre-Cache für Videos senden.
...
return fetch(videoFileUrl, { headers: { range: 'bytes=0-567139' } })
.then(networkResponse => networkResponse.arrayBuffer())
.then(data => {
const response = new Response(data);
// Add the response to the cache and return network response in parallel.
cache.put(videoFileUrl, response.clone());
return response;
});
Video abspielen
Wenn ein Nutzer auf eine Wiedergabeschaltfläche klickt, wird das erste Segment des Videos abgerufen. in der Cache-API verfügbar, sodass die Wiedergabe sofort startet, falls verfügbar. Andernfalls rufen wir sie einfach aus dem Netzwerk ab. Denken Sie daran, dass Browser Nutzer können sich entscheiden, den Cache zu leeren.
Wie bereits erwähnt, nutzen wir MSE, um das erste Videosegment in das Video einzuspeisen. -Elements.
function onPlayButtonClick(videoFileUrl) {
video.load(); // Used to be able to play video later.
window.caches.open('video-pre-cache')
.then(cache => fetchAndCache(videoFileUrl, cache)) // Defined above.
.then(response => response.arrayBuffer())
.then(data => {
const mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', sourceOpen, { once: true });
function sourceOpen() {
URL.revokeObjectURL(video.src);
const sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vp09.00.10.08"');
sourceBuffer.appendBuffer(data);
video.play().then(() => {
// TODO: Fetch the rest of the video when user starts playing video.
});
}
});
}
Bereichsantworten mit einem Service Worker erstellen
Was ist, wenn ihr eine ganze Videodatei abgerufen und
die Cache-API? Wenn der Browser eine HTTP-Range
-Anfrage sendet,
das gesamte Video in den Arbeitsspeicher des Renderers übertragen werden, da die Cache API
unterstützen noch Range
-Antworten.
Ich zeige Ihnen nun, wie Sie diese Anfragen abfangen und eine benutzerdefinierte Range
-Anfrage zurückgeben können.
von einem Service Worker erhalten.
addEventListener('fetch', event => {
event.respondWith(loadFromCacheOrFetch(event.request));
});
function loadFromCacheOrFetch(request) {
// Search through all available caches for this request.
return caches.match(request)
.then(response => {
// Fetch from network if it's not already in the cache.
if (!response) {
return fetch(request);
// Note that we may want to add the response to the cache and return
// network response in parallel as well.
}
// Browser sends a HTTP Range request. Let's provide one reconstructed
// manually from the cache.
if (request.headers.has('range')) {
return response.blob()
.then(data => {
// Get start position from Range request header.
const pos = Number(/^bytes\=(\d+)\-/g.exec(request.headers.get('range'))[1]);
const options = {
status: 206,
statusText: 'Partial Content',
headers: response.headers
}
const slicedResponse = new Response(data.slice(pos), options);
slicedResponse.setHeaders('Content-Range': 'bytes ' + pos + '-' +
(data.size - 1) + '/' + data.size);
slicedResponse.setHeaders('X-From-Cache': 'true');
return slicedResponse;
});
}
return response;
}
}
Wichtig: Ich habe response.blob()
verwendet, um diese segmentierte Datei neu zu erstellen.
Dadurch erhalte ich
einen Handle auf die Datei, während
Mit response.arrayBuffer()
wird die gesamte Datei in den Arbeitsspeicher des Renderers übertragen.
Mit meinem benutzerdefinierten X-From-Cache
-HTTP-Header kann ermittelt werden, ob diese Anfrage
aus dem Cache
oder aus dem Netzwerk stammt. Es kann von einem Player wie
ShakaPlayer, um die Antwortzeit als Indikator für
Netzwerkgeschwindigkeit.
Sieh dir die offizielle Beispielmedien-App und insbesondere die
ranged-response.js enthält eine Komplettlösung für den Umgang mit Range
-Anfragen.