Lewati ke konten
Teknologi Pengembang
  • Rumah
  • teknologi

Pemutaran cepat dengan pramuat audio dan video

4 Juni 2021 Artikel

How to accelerate your media playback by actively preloading resources.

Aug 17, 2017• Diperbarui Nov 16, 2020

Muncul di: Waktu muat cepat|Media
François Beaufort

Faster playback start means more people watching your video or listening to your audio. That’s a known fact. In this article I’ll explore techniques you can use to accelerate your audio and video playback by actively preloading resources depending on your use case.

Credits: copyright Blender Foundation | www.blender.org .

I’ll describe three methods of preloading media files, starting with their pros and cons.

It’s great… But…
Video preload attribute Simple to use for a unique file hosted on a web server. Browsers may completely ignore the attribute.
Resource fetching starts when the HTML document has been completely loaded and parsed.
Media Source Extensions (MSE) ignore the pramuat attribute on media elements because the app is responsible for providing media to MSE.
Link preload Forces the browser to make a request for a video resource without blocking the document’s onload event. HTTP Range requests are not compatible.
Compatible with MSE and file segments. Should be used only for small media files (
Manual buffering Full control Complex error handling is the website’s responsibility.

Isi

  • 1 Video preload attribute #
    • 1.1 Tips #
  • 2 Link preload #
    • 2.1 Preload full video #
    • 2.2 Preload the first segment #
    • 2.3 Support #
  • 3 Manual buffering #
    • 3.1 Considerations #
      • 3.1.1 Battery awareness #
      • 3.1.2 Detect “Data-Saver” #
      • 3.1.3 Smart loading based on network information #
    • 3.2 Pre-cache multiple first segments #
      • 3.2.1 Fetch and cache #
      • 3.2.2 Play video #
    • 3.3 Create Range responses with a service worker #

Video preload attribute #

If the video source is a unique file hosted on a web server, you may want to use the video pramuat attribute to provide a hint to the browser as to how much information or content to preload. This means Media Source Extensions (MSE) is not compatible with pramuat.

Resource fetching will start only when the initial HTML document has been completely loaded and parsed (e.g. the DOMKontenDimuat event has fired) while the very different memuat event will be fired when resource has actually been fetched.

Setting the pramuat attribute to metadata indicates that the user is not expected to need the video, but that fetching its metadata (dimensions, track list, duration, and so on) is desirable. Note that starting in Chrome 64, the default value for pramuat is metadata. (It was auto previously).

video id="video" preload="metadata" src="file.mp4" controls>/video>

script>
video.addEventListener('loadedmetadata', function() {
if (video.buffered.length === ) return;

const bufferedSeconds = video.buffered.end() - video.buffered.start();
console.log(`${bufferedSeconds} seconds of video are ready to play.`);
});
/script>

Setting the pramuat attribute to auto indicates that the browser may cache enough data that complete playback is possible without requiring a stop for further buffering.

video id="video" preload="auto" src="file.mp4" controls>/video>

script>
video.addEventListener('loadedmetadata', function() {
if (video.buffered.length === ) return;

const bufferedSeconds = video.buffered.end() - video.buffered.start();
console.log(`${bufferedSeconds} seconds of video are ready to play.`);
});
/script>

There are some caveats though. As this is just a hint, the browser may completely ignore the pramuat attribute. At the time of writing, here are some rules applied in Chrome:

  • When Data Saver is enabled, Chrome forces the pramuat value to none.
  • In Android 4.3, Chrome forces the pramuat value to none due to an Android Bug.
  • On a cellular connection (2G, 3G, and 4G), Chrome forces the pramuat value to metadata.

Tips #

If your website contains many video resources on the same domain, I would recommend you set the pramuat value to metadata or define the poster attribute and set pramuat ke none. That way, you would avoid hitting the maximum number of HTTP connections to the same domain (6 according to the HTTP 1.1 spec) which can hang loading of resources. Note that this may also improve page speed if videos aren’t part of your core user experience.

Link preload #

As covered in other articles, link preload is a declarative fetch that allows you to force the browser to make a request for a resource without blocking the memuat event and while the page is downloading. Resources loaded via are stored locally in the browser, and are effectively inert until they’re explicitly referenced in the DOM, JavaScript, or CSS.

Preload is different from prefetch in that it focuses on current navigation and fetches resources with priority based on their type (script, style, font, video, audio, etc.). It should be used to warm up the browser cache for current sessions.

Preload full video #

Here’s how to preload a full video on your website so that when your JavaScript asks to fetch video content, it is read from cache as the resource may have already been cached by the browser. If the preload request hasn’t finished yet, a regular network fetch will happen.

link rel="preload" sebagai="video" href="https://cdn.com/small-file.mp4">

video id="video" controls>/video>

script>
video.src = 'https://cdn.com/small-file.mp4';
video.play().kemudian(() => {
});
/script>

I would recommend using this only for small media files (less than 5MB).

Because the preloaded resource is going to be consumed by a video element in the example, the sebagai preload link value is video. If it were an audio element, it would be as="audio".

Preload the first segment #

The example below shows how to preload the first segment of a video with and use it with Media Source Extensions. If you’re not familiar with the MSE JavaScript API, see MSE basics.

For the sake of simplicity, let’s assume the entire video has been split into smaller files like file_1.webm, file_2.webm, file_3.webm, etc.

link rel="preload" sebagai="fetch" href="https://cdn.com/file_1.webm">

video id="video" controls>/video>

script>
konstan mediaSource = baru MediaSource();
video.src = URL.createObjectURL(mediaSource);
mediaSource.tambahkanEventListener('sourceopen', sourceOpen, { once: benar });

fungsi sourceOpen() {
URL.revokeObjectURL(video.src);
konstan sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vp09.00.10.08"');


fetch('https://cdn.com/file_1.webm')
.kemudian(response => response.arrayBuffer())
.kemudian(data => {
sourceBuffer.appendBuffer(data);
})
.catch(kesalahan => {
});
}
/script>

Peringatan: For cross-origin resources, make sure your CORS headers are set properly. As we can’t create an array buffer from an opaque response retrieved with fetch(videoFileUrl, { mode: 'no-cors' }), we won’t be able to feed any video or audio element.

Mendukung #

See MDN’s Browser compatibility table to see which browsers support preload. You may want to detect its availability with the snippets below to adjust your performance metrics.

fungsi preloadFullVideoSupported() {
konstan link = dokumen.createElement('link');
link.sebagai = 'video';
kembali (link.sebagai === 'video');
}

fungsi preloadFirstSegmentSupported() {
konstan link = dokumen.createElement('link');
link.sebagai = 'fetch';
kembali (link.sebagai === 'fetch');
}

Manual buffering #

Before we dive into the Cache API and service workers, let’s see how to manually buffer a video with MSE. The example below assumes that your web server supports HTTP Range requests but this would be pretty similar with file segments. Note that some middleware libraries such as Google’s Shaka Player, JW Player, dan Video.js are built to handle this for you.

video id="video" controls>/video>

script>
konstan mediaSource = baru MediaSource();
video.src = URL.createObjectURL(mediaSource);
mediaSource.tambahkanEventListener('sourceopen', sourceOpen, { once: benar });

fungsi sourceOpen() {
URL.revokeObjectURL(video.src);
konstan sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vp09.00.10.08"');


fetch('file.webm', { headers: { range: 'bytes=0-567139' } })
.kemudian(response => response.arrayBuffer())
.kemudian(data => {
sourceBuffer.appendBuffer(data);
sourceBuffer.tambahkanEventListener('updateend', updateEnd, { once: benar });
});
}

fungsi updateEnd() {
konstan bufferedSeconds = video.buffered.end() - video.buffered.start();
menghibur.catatan(`${bufferedSeconds} seconds of video are ready to play.`);


video.tambahkanEventListener('playing', fetchNextSegment, { once: benar });
}

fungsi fetchNextSegment() {
fetch('file.webm', { headers: { range: 'bytes=567140-1196488' } })
.kemudian(response => response.arrayBuffer())
.kemudian(data => {
konstan sourceBuffer = mediaSource.sourceBuffers[];
sourceBuffer.appendBuffer(data);
});
}
/script>

Considerations #

As you’re now in control of the entire media buffering experience, I suggest you consider the device’s battery level, the “Data-Saver Mode” user preference and network information when thinking about preloading.

Battery awareness #

Take into account the battery level of users’ devices before thinking about preloading a video. This will preserve battery life when the power level is low.

Disable preload or at least preload a lower resolution video when the device is running out of battery.

jika ('getBattery' in navigator) {
navigator.getBattery()
.kemudian(battery => {
jika (battery.charging || battery.level > 0.15) {
}
});
}

Detect “Data-Saver” #

Use the Save-Data client hint request header to deliver fast and light applications to users who have opted-in to “data savings” mode in their browser. By identifying this request header, your application can customize and deliver an optimized user experience to cost- and performance-constrained users.

See Delivering Fast and Light Applications with Save-Data to learn more.

Smart loading based on network information #

You may want to check navigator.connection.type prior to preloading. When it’s set to cellular, you could prevent preloading and advise users that their mobile network operator might be charging for the bandwidth, and only start automatic playback of previously cached content.

jika ('connection' in navigator) {
jika (navigator.connection.type == 'cellular') {
} kalau tidak {
}
}

Lihat Network Information sample to learn how to react to network changes as well.

Pre-cache multiple first segments #

Now what if I want to speculatively pre-load some media content without knowing which piece of media the user will eventually pick? If the user is on a web page that contains 10 videos, we probably have enough memory to fetch one segment file from each but we should definitely not create 10 hidden elements and 10 MediaSource objects and start feeding that data.

The two-part example below shows you how to pre-cache multiple first segments of video using the powerful and easy-to-use Cache API. Note that something similar can be achieved with IndexedDB as well. We’re not using service workers yet as the Cache API is also accessible from the jendela object.

Fetch and cache #

konstan videoFileUrls = [
'bat_video_file_1.webm',
'cow_video_file_1.webm',
'dog_video_file_1.webm',
'fox_video_file_1.webm',
];


jendela.caches.membuka('video-pre-cache')
.kemudian(cache => Janji.semua(videoFileUrls.map(videoFileUrl => fetchAndCache(videoFileUrl, cache))));

fungsi fetchAndCache(videoFileUrl, cache) {
kembali cache.cocok(videoFileUrl)
.kemudian(cacheResponse => {
jika (cacheResponse) {
kembali cacheResponse;
}
kembali fetch(videoFileUrl)
.kemudian(networkResponse => {
cache.put(videoFileUrl, networkResponse.clone());
kembali networkResponse;
});
});
}

Note that if I were to use HTTP Range requests, I would have to manually recreate a Response object as the Cache API doesn’t support Range responses yet. Be mindful that calling networkResponse.arrayBuffer() fetches the whole content of the response at once into renderer memory, which is why you may want to use small ranges.

For reference, I’ve modified part of the example above to save HTTP Range requests to the video precache.

    ...
kembali fetch(videoFileUrl, { headers: { range: 'bytes=0-567139' } })
.kemudian(networkResponse => networkResponse.arrayBuffer())
.kemudian(data => {
konstan response = baru Response(data);
cache.put(videoFileUrl, response.clone());
kembali response;
});

Play video #

When a user clicks a play button, we’ll fetch the first segment of video available in the Cache API so that playback starts immediately if available. Otherwise, we’ll simply fetch it from the network. Keep in mind that browsers and users may decide to clear the Cache.

As seen before, we use MSE to feed that first segment of video to the video element.

fungsi onPlayButtonClick(videoFileUrl) {
video.memuat();

jendela.caches.membuka('video-pre-cache')
.kemudian(cache => fetchAndCache(videoFileUrl, cache))
.kemudian(response => response.arrayBuffer())
.kemudian(data => {
konstan mediaSource = baru MediaSource();
video.src = URL.createObjectURL(mediaSource);
mediaSource.tambahkanEventListener('sourceopen', sourceOpen, { once: benar });

fungsi sourceOpen() {
URL.revokeObjectURL(video.src);

konstan sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vp09.00.10.08"');
sourceBuffer.appendBuffer(data);

video.play().kemudian(() => {
});
}
});
}

Peringatan: For cross-origin resources, make sure your CORS headers are set properly. As we can’t create an array buffer from an opaque response retrieved with fetch(videoFileUrl, { mode: 'no-cors' }), we won’t be able to feed any video or audio element.

Create Range responses with a service worker #

Now what if you have fetched an entire video file and saved it in the Cache API? When the browser sends an HTTP Range request, you certainly don’t want to bring the entire video into renderer memory as the Cache API doesn’t support Range responses yet.

So let me show how to intercept these requests and return a customized Range response from a service worker.

tambahkanEventListener('fetch', peristiwa => {
peristiwa.respondWith(loadFromCacheOrFetch(peristiwa.request));
});

fungsi loadFromCacheOrFetch(request) {
kembali caches.cocok(request)
.kemudian(response => {


jika (!response) {
kembali fetch(request);
}


jika (request.headers.has('range')) {
kembali response.blob()
.kemudian(data => {


konstan pos = Number(/^bytes=(d+)-/g.exec(request.headers.get('range'))[1]);
konstan options = {
status: 206,
statusText: 'Partial Content',
headers: response.headers
}
konstan slicedResponse = baru Response(data.slice(pos), options);
slicedResponse.setHeaders('Content-Range': 'bytes ' + pos + '-' +
(data.size - 1) + '/' + data.size);
slicedResponse.setHeaders('X-From-Cache': 'true');

kembali slicedResponse;
});
}

kembali response;
}
}

It is important to note that I used response.blob() to recreate this sliced response as this simply gives me a handle to the file while response.arrayBuffer() brings the entire file into renderer memory.

My custom X-From-Cache HTTP header can be used to know whether this request came from the cache or from the network. It can be used by a player such as ShakaPlayer to ignore the response time as an indicator of network speed.

Have a look at the official Sample Media App and in particular its ranged-response.js file for a complete solution for how to handle Range requests.

Terakhir diperbarui: Nov 16, 2020Perbaiki artikel

BACA Juga :   Halo Dunia!

Kalender

Maret 2022
S S R K J S M
 123456
78910111213
14151617181920
21222324252627
28293031  
« Februari    

Arsip

  • Maret 2022
  • Februari 2022
  • Januari 2022
  • November 2021
  • Oktober 2021
  • Agustus 2021
  • Juli 2021
  • Juni 2021

Kategori

  • Perbaiki artikel
  • Pertunjukan
  • teknologi
  • Tidak berkategori
  • Wordpress

hak cipta Dev Tech 2022 | Oleh Tema sedang berlangsung | Dengan bangga didukung oleh WordPress

id_IDIndonesian
en_USEnglish id_IDIndonesian