리소스를 적극적으로 미리 로드하여 미디어 재생을 가속화하는 방법
<ph type="x-smartling-placeholder">
재생이 빨라지면 더 많은 사용자가 동영상을 보거나 오디오입니다. 이는 알려진 사실입니다. 이 도움말에서는 오디오 및 동영상 재생의 속도를 높이는 데 사용할 수 있는 리소스를 미리 로드할 수 있습니다
미디어 파일을 미리 로드하는 세 가지 방법인 장점부터 설명하겠습니다. 그리고 단점이 있습니다
멋진데... | 하지만... | |
---|---|---|
동영상 미리 로드 속성 | 웹 서버에서 호스팅되는 고유한 파일에 간편하게 사용할 수 있습니다. | 브라우저에서 이 속성을 완전히 무시할 수도 있습니다. |
HTML 문서가 완전히 로드되면 리소스 가져오기가 시작되고 파싱할 수 있습니다. | ||
앱이 미디어 소스 확장 프로그램 (MSE)에서 미디어 요소의 preload 속성을 무시함
MSE에 미디어를 제공합니다
|
||
링크 미리 로드 |
브라우저가 차단 없이 동영상 리소스를 요청하도록 강제합니다.
문서의 onload 이벤트
|
HTTP 범위 요청은 호환되지 않습니다. |
MSE 및 파일 세그먼트와 호환됩니다. | 전체 리소스를 가져올 때 작은 미디어 파일(5MB 미만)에만 사용해야 합니다. | |
수동 버퍼링 | 전체 제어 | 복잡한 오류 처리는 웹사이트의 책임입니다. |
동영상 미리 로드 속성
동영상 소스가 웹 서버에서 호스팅되는 고유한 파일인 경우
동영상 preload
속성을 사용하여 브라우저에 힌트를
많은 정보나 콘텐츠를 미리 로드합니다. 즉, 미디어 소스 확장 프로그램은
(MSE)는 preload
과 호환되지 않습니다.
리소스 가져오기는 초기 HTML 문서가 작성된 경우에만 시작됩니다.
완전히 로드 및 파싱됨 (예: DOMContentLoaded
이벤트 실행됨)
매우 다른 load
이벤트는 리소스가
확인할 수 있습니다
preload
속성을 metadata
로 설정하면 사용자가
동영상이 필요하지만 메타데이터 (크기, 트랙
목록, 기간 등)을 지정하는 것이 바람직합니다. Chrome에서
64라면 preload
의 기본값은 metadata
입니다. (이전 가격: auto
참고).
<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>
preload
속성을 auto
로 설정하면 브라우저가 캐시할 수 있음을 나타냅니다.
충분한 데이터를 재생하기 전에
추가 버퍼링이 발생할 수 있습니다.
<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>
하지만 몇 가지 주의사항이 있습니다. 이는 힌트에 불과하므로 브라우저에서
preload
속성은 무시합니다. 이 문서를 작성할 당시 다음과 같은 몇 가지 규칙이 있습니다.
Chrome에서 다음과 같이 표시됩니다.
- 데이터 절약 모드를 사용 설정하면 Chrome에서
preload
값을none
입니다. - Android 4.3에서는 Android 오류로 인해 Chrome에서
preload
값을none
로 강제합니다. 버그. - 모바일 데이터 연결 (2G, 3G, 4G)에서 Chrome은
preload
값을 다음과 같이 강제합니다.metadata
팁
웹사이트에 동일한 도메인의 동영상 리소스가 많이 있는 경우
preload
값을 metadata
로 설정하거나 poster
를 정의하는 것이 좋습니다.
속성을 사용하고 preload
를 none
로 설정합니다. 이렇게 하면
동일한 도메인에 대한 최대 HTTP 연결 수 (6개의
HTTP 1.1 사양)이 있을 수 있으므로 리소스 로드가 지연될 수 있습니다. 또한
동영상이 핵심 사용자 환경의 일부가 아닌 경우 페이지 속도 향상
링크 미리 로드
<ph type="x-smartling-placeholder">다른 도움말에서 다룬 바와 같이 링크 미리 로드는 선언적 가져오기로
를 사용하면
페이지가 다운로드되고 있을 때 load
이벤트를 차단할 수 있습니다. 리소스
<link rel="preload">
를 통해 로드된 데이터는 브라우저에 로컬로 저장되며
DOM, JavaScript 및 JavaScript에서 명시적으로 참조될 때까지 효과적으로 비활성됩니다.
또는 CSS 등이 있습니다.
미리 로드는 현재 탐색에 집중한다는 점에서 미리 가져오기와 다릅니다. 유형 (스크립트, 스타일, 글꼴, 동영상, 오디오 등) 현재 실행 중인 웹 서버에 대해 브라우저 캐시를 예열하는 데 세션.
전체 동영상 미리 로드
전체 동영상을 웹사이트에 미리 로드하여 JavaScript가 동영상 콘텐츠를 가져오도록 요청하는 경우 리소스로서 캐시에서 읽습니다. 브라우저에 의해 이미 캐시되었을 수 있습니다. 미리 로드 요청이 제대로 아직 완료되지 않으면 일반 네트워크 가져오기가 발생합니다.
<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>
미리 로드된 리소스는
이 예에서 as
미리 로드 링크 값은 video
입니다. 오디오였다면
요소에서는 as="audio"
가 됩니다.
첫 번째 세그먼트 미리 로드
아래 예는 <link
rel="preload">
를 사용하여 동영상의 첫 번째 세그먼트를 미리 로드하고 미디어 소스 확장 프로그램과 함께 사용하는 방법을 보여줍니다. 이
MSE JavaScript API를 사용하는 경우 MSE 기본사항을 참고하세요.
편의상 전체 동영상이 다음과 같이 분할되었다고 가정해 보겠습니다.
file_1.webm
, file_2.webm
, file_3.webm
등의 더 작은 파일
<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>
지원
<ph type="x-smartling-placeholder">다음을 사용하여 <link rel=preload>
의 다양한 as
유형 지원을 감지할 수 있습니다.
다음 코드를 참조하세요.
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');
}
수동 버퍼링
Cache API와 서비스 워커에 대해 알아보기 전에
MSE로 동영상을 수동으로 버퍼링하는 방법 아래 예에서는 웹이
서버가 HTTP Range
를 지원합니다.
이 작업은
세그먼트. Google의 Shaka와 같은 일부 미들웨어 라이브러리는
플레이어, JW Player, Video.js는
빌드됩니다
<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>
고려사항
이제 전체 미디어 버퍼링 경험을 관리할 수 있으므로 기기의 배터리 잔량, 즉 '데이터 절약 모드' 사용자 환경설정 및 네트워크 정보를 확인하시기 바랍니다.
배터리 인식
사용자의 배터리 잔량을 고려합니다. 생각하기 전에 자세히 살펴보겠습니다 이렇게 하면 전원 수준이 되었을 때 배터리 수명을 보존할 수 있습니다. 감소합니다.
사전 로드를 사용 중지하거나 최소한 해상도가 낮은 동영상을 미리 로드한 상태에서 기기의 배터리가 부족합니다.
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.
}
});
}
'데이터 절약 모드' 감지
Save-Data
클라이언트 힌트 요청 헤더를 사용하여 빠르고 가벼운 기능 제공
'데이터 절약'을 선택한 사용자를 대상으로 하는 애플리케이션입니다. 모드를
있습니다. 이 요청 헤더를 식별함으로써 애플리케이션은
비용 및 성능이 제한된 사용자에게 최적화된 사용자 경험을 제공합니다.
있습니다.
자세한 내용은 데이터 저장을 사용하여 빠르고 가벼운 애플리케이션 제공을 참조하세요.
네트워크 정보에 기반한 스마트 로드
미리 로드하기 전에 navigator.connection.type
를 확인하는 것이 좋습니다. 날짜
cellular
로 설정되어 있으면 미리 로드를 방지하고 사용자에게
해당 모바일 네트워크 운영자가 대역폭에 대해 충전하고 있을 수 있으며
이전에 캐시된 콘텐츠의 자동 재생
if ('connection' in navigator) {
if (navigator.connection.type == 'cellular') {
// TODO: Prompt user before preloading video
} else {
// TODO: Preload the first segment of a video.
}
}
네트워크 정보 샘플에서 네트워크에 대응하는 방법을 알아보세요. 확인할 수 있습니다
여러 첫 번째 세그먼트 사전 캐시
이제 일부 미디어 콘텐츠를 추측성으로 미리 로드하려면
어떻게 해야 할까요?
사용자가 결국 어떤 미디어를 선택할지
알고 있나요? 사용자가
10개의 동영상이 포함된 웹페이지의 경우 동영상을 하나 가져오는 데 충분한 메모리가
세그먼트 파일을 만들었지만 숨겨진 <video>
를 10개 생성해서는 안 됩니다.
요소 및 10개의 MediaSource
객체를 사용하고 해당 데이터를 제공하기 시작합니다.
아래의 두 부분으로 구성된 예는 Google Cloud의
강력하고 사용하기 쉬운 Cache API를 사용하여 동영상을 캐시할 수 있습니다. 참고로
IndexedDB로도 얻을 수 있습니다. 서비스 워커는 아직
window
객체에서도 Cache API에 액세스할 수 있습니다.
가져오기 및 캐시
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;
});
});
}
HTTP Range
요청을 사용하려면 수동으로
Response
객체는 아직 Range
응답을 지원하지 않기 때문입니다. CANNOT TRANSLATE
networkResponse.arrayBuffer()
를 호출하면 전체 콘텐츠를 가져온다는 점에 유의하세요.
렌더러 메모리로 한 번에 복사해야 하므로
작은 범위입니다
참고로 HTTP 범위를 저장하기 위해 위 예제의 일부를 수정했습니다. 동영상 사전 캐시에 대한 요청을 전송합니다
...
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;
});
동영상 재생
사용자가 재생 버튼을 클릭하면 YouTube가 동영상의 첫 번째 세그먼트를 가능한 경우 즉시 재생이 시작되도록 Cache API에서 사용할 수 있습니다. 그렇지 않으면 네트워크에서 가져옵니다. 브라우저는 사용자는 캐시를 삭제할 수 있습니다.
앞에서 본 것처럼 MSE를 사용하여 동영상의 첫 번째 세그먼트를 동영상에 피드합니다. 요소가 포함됩니다.
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.
});
}
});
}
서비스 워커로 Range 응답 만들기
이제 전체 동영상 파일을 가져와서
어떻게 해야 할까요? 브라우저가 HTTP Range
요청을 보낼 때는
Cache API에서 지원하지 않기 때문에
전체 동영상을 렌더기 메모리로 가져오려고 할 때
아직 Range
응답을 지원하지 않습니다.
이러한 요청을 가로채서 맞춤설정된 Range
를 반환하는 방법을 보여드리겠습니다.
요청을 보낼 수 있습니다
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;
}
}
중요한 점은 response.blob()
를 사용하여 이 슬라이스를 다시 만든다는 것입니다.
이렇게 하면 파일이 전송되는 동안
response.arrayBuffer()
는 전체 파일을 렌더기 메모리로 가져옵니다.
내 커스텀 X-From-Cache
HTTP 헤더를 사용하면 이 요청이
캐시나 네트워크에서 가져왔는지 확인합니다. 다음과 같은 플레이어에서 사용할 수 있습니다.
ShakaPlayer를 호출하여 응답 시간을
네트워크 속도입니다.
공식 샘플 미디어 앱을 살펴보고 특히
Range
처리 방법에 관한 완전한 솔루션을 제공하는 ranged-response.js 파일
있습니다