video 分片加载

news/发布时间2024/5/19 21:01:44
  • API使用: MediaSource + SourceBuffer

  • client.html

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body><video id="video" controls width="500" height="300"></video><script>/** @type {HTMLVideoElement} */const video = document.getElementById('video');const mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';init();async function init() {const filesize = await getFileSize();const chunckSize = 1024 * 1024 * 1; // 1MB// 分片片数const chuncks = Math.ceil(filesize / chunckSize);let start = 0,end = chunckSize + start;const url = 'http://localhost:3000';/** @type {SourceBuffer} */let sourceBuffer = null;/** @type {MediaSource} */let mediaSource = null;let preloadTime = 0;function handleProgress() {preloadTime = sourceBuffer.buffered.end(0);console.log('preloadTime => ', preloadTime);};let timer = null;function handlePlaying() {if (timer) {clearInterval(timer);timer = null;}timer = setInterval(() => {// 当播放位置与当前 SourceBuffer 中已缓冲区的最后一个片段的结尾处相差不超过 2 秒时,则开始下载下一个分片if (video.currentTime >= preloadTime - 2) {clearInterval(timer);timer = null;start = end + 1;end = start + chunckSize;if (start > filesize - 1) {// 流媒体结束sourceBuffer.abort();mediaSource.endOfStream();mediaSource = null;sourceBuffer = null;return;}handlePlaying();handle();}}, 300);}video.addEventListener('seeking', async () => {console.log('video.currentTime => ', video.currentTime);if (video.currentTime <= preloadTime) return;});video.addEventListener('play', handlePlaying);video.addEventListener('progress', handleProgress);handle();async function handle() {if (end > filesize - 1) {end = filesize - 1;}console.log('start, end => ', start, end, filesize);const partBlob = await ajax(url, {headers: { Range: `bytes=${start}-${end}` },});const arrayBuffer = await partBlob.arrayBuffer();if (!sourceBuffer) {let { sourceBuffer: _buf, mediaSource: _ms } = await handleMediaSource();sourceBuffer = _buf;mediaSource = _ms;// video.play();}// 将分片数据添加到 SourceBuffer 对象中// 方法将 `ArrayBuffer、TypedArray 或 DataView` 中的媒体片段数据添加到 SourceBuffer 对象中sourceBuffer.appendBuffer(arrayBuffer);}}function decounce(func, delay = 1000) {let timer = null;return (...args) => {if (timer) {clearTimeout(timer);}timer = setTimeout(() => {func(...args);}, delay);};}function handleMediaSource() {return new Promise((resolve) => {const mediaSource = new MediaSource();console.log('isTypeSupported => ', MediaSource.isTypeSupported(mimeCodec));mediaSource.addEventListener('sourceopen', () => {// 根据给定的 MIME 类型创建一个新的 `SourceBuffer` 对象// SourceBuffer: 通过 `MediaSource` 对象传递到 `HTMLMediaElement` 并播放的媒体分块const sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);sourceBuffer.addEventListener('error', (e) => {console.log('sourceBuffer error', e);});// sourceBuffer.appendBuffer(arrayBuffer);resolve({ sourceBuffer, mediaSource });});video.src = URL.createObjectURL(mediaSource);});}async function getFileSize() {const url = 'http://localhost:3000/filesize';const blob = await ajax(url);const size = await blob.text();return +size;}function ajax(url, options = {}) {return fetch(url, { ...options }).then((res) => res.blob());}</script></body>
</html>
  • serve.js
import { serve } from 'bun';
import { createReadStream } from 'node:fs';
import { stat } from 'node:fs/promises';
import { URL } from 'node:url';const filePath = './frag_bunny.mp4';
const fileContentType = 'video/mp4';const { size: fileSize } = await stat(filePath);const server = serve({port: 3000,async fetch(req) {const { pathname } = new URL(req.url);console.log(' => ', req.method, pathname);if (pathname === '/filesize') {return new Response(fileSize.toString(), {headers: {'Access-Control-Allow-Origin': '*',},});}const range = req.headers.get('range');console.log('range => ', range);let [start, end] = [0, fileSize - 1];if (range) {const rangeStr = range.split('=')[1];const reg = /^bytes=\d+$/;if (reg.test(range)) {end = start = parseInt(rangeStr);} else {[start, end] = rangeStr.split('-');if (start === '' && end !== '') {start = fileSize - end;end = fileSize - 1;} else {start = start === '' ? fileSize - end : parseInt(start);end = end === '' ? fileSize - 1 : parseInt(end);}}console.log('start, end => ', start, end, fileSize);if (end > fileSize - 1 || start > fileSize - 1 || start > end || start < 0 || end < 0) {return new Response('请求范围不合法', { status: 416 });}}return new Response(createReadStream(filePath, { start, end }), {status: 206,headers: {'Access-Control-Allow-Origin': '*','Access-Control-Allow-Headers': 'Range','Content-Type': fileContentType,'Content-Length': end - start + 1,'Content-Range': `bytes ${start}-${end}/${fileSize}`,'Accept-Ranges': 'bytes',},});},
});console.log(`Listening on http://localhost:${server.port}`);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ulsteruni.cn/article/13061586.html

如若内容造成侵权/违法违规/事实不符,请联系编程大学网进行投诉反馈email:xxxxxxxx@qq.com,一经查实,立即删除!

相关文章

实验二。

include <stdio.h> include<stdlib.h> include<time.h> define N 5 int main() { int number; int i; srand(time(0));for(i=0;i<N;++i){ number=rand()%65+1; printf("20238331%04d\n",number); } return 0;}问题一:一到六十五之间随…

httprunner 4.x学习 - 05校验(validate)

前言 HttpRunner4.x 内置了丰富的校验结果的方式 校验方式assert缩写说明equal "eq", "equals", "equal" 相等less_than "lt", "less_than" 小于less_or_equals "le", "less_or_equals" 小于或等于grea…

继续MDT的bug,

简介 这个据说是多播的bug 如果你真的想使用多重广播,这是我如何解决这个问题的。获取 Windows 11 ISO (x64) 挂载 ISO,在 sources 文件夹中,您需要 2 个文件wdscommon.dll和imagelib.dll 将这些文件复制到 x64 文件夹> mdt 部署共享>工具(例如,在我的文件夹中,…

建设库数据爬取

1. python部分: # -*- coding:utf-8 -*-# @Time : 2024/4/14 17:57 # @Author : 快乐的小猴子 # @Version : # @Function : import requests import json import subprocess from functools import partial # 专门用来固定参数的 subprocess.Popen = partial(subprocess.Po…

[ABC349] AtCoder Beginner Contest 349 题解

最近AT怎么这么寄[ABC349] AtCoder Beginner Contest 349 题解 目录[ABC349] AtCoder Beginner Contest 349 题解A - Zero Sum GameB - CommencementC - Airport CodeD - Divide IntervalE - Weighted Tic-Tac-ToeF - Subsequence LCMG - Palindrome Construction总结 A - Zero…

拆分内容

问题:将以下在一个单元格中的内容按品名、数量、单价、金额拆分出来函数公式解决:B2公式 =LEFT(TEXTBEFORE(A2,""),LEN(TEXTBEFORE(A2,""))-LEN(C2)) C2公式 =-LOOKUP(1,-RIGHT(TEXTBEFORE(A2,""),SEQUENCE(99))) D2公式 =-LOOKUP(1,-LEFT(TEX…