demuxe 0.3.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +352 -0
- package/README.md +75 -0
- package/bin/demuxe.mjs +30 -0
- package/docs/API-MIGRATION.md +54 -0
- package/docs/BETA.md +98 -0
- package/docs/BRANDING-MIGRATION.md +77 -0
- package/docs/COMPATIBILITY-EXPANSION.md +181 -0
- package/docs/LICENSING.md +83 -0
- package/docs/OPTIMIZATION-COMPLETION.md +511 -0
- package/docs/OPTIMIZATION-FLAC.md +178 -0
- package/docs/OPTIMIZATION-INTEGRATION.md +221 -0
- package/docs/OPTIMIZATION-REVIEW-FIXES.md +78 -0
- package/docs/PLAYER-COMPONENT.md +247 -0
- package/docs/PUBLIC-API-VALIDATION.md +163 -0
- package/docs/PUBLIC-API.md +245 -0
- package/docs/RELEASE.md +181 -0
- package/docs/RUNTIME-ASSETS.md +127 -0
- package/engine-build.json +8781 -0
- package/examples/custom-controls.html +14 -0
- package/examples/player-element.html +5 -0
- package/fixtures/DejaVuSans.ttf +0 -0
- package/fixtures/FONT-LICENSE.txt +187 -0
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/package.json +46 -0
- package/player.d.ts +1 -0
- package/player.js +1 -0
- package/release-manifest.json +541 -0
- package/sources.lock.json +96 -0
- package/third_party/notices/dav1d/COPYING +23 -0
- package/third_party/notices/emscripten/LICENSE +102 -0
- package/third_party/notices/emscripten/system/lib/compiler-rt/LICENSE.TXT +311 -0
- package/third_party/notices/emscripten/system/lib/libc/musl/COPYRIGHT +193 -0
- package/third_party/notices/emscripten/system/lib/libcxx/LICENSE.TXT +311 -0
- package/third_party/notices/emscripten/system/lib/libcxxabi/LICENSE.TXT +311 -0
- package/third_party/notices/emsdk/LICENSE +27 -0
- package/third_party/notices/ffmpeg/COPYING.GPLv2 +339 -0
- package/third_party/notices/ffmpeg/COPYING.GPLv3 +674 -0
- package/third_party/notices/ffmpeg/COPYING.LGPLv2.1 +502 -0
- package/third_party/notices/ffmpeg/LICENSE.md +129 -0
- package/third_party/notices/ffmpeg-adaptation/COPYING.LGPLv2.1 +502 -0
- package/third_party/notices/ffmpeg-adaptation/LICENSE.md +127 -0
- package/third_party/notices/freetype/LICENSE.TXT +46 -0
- package/third_party/notices/freetype/docs/FTL.TXT +169 -0
- package/third_party/notices/freetype/docs/GPLv2.TXT +340 -0
- package/third_party/notices/fribidi/COPYING +504 -0
- package/third_party/notices/harfbuzz/COPYING +42 -0
- package/third_party/notices/libass/COPYING +15 -0
- package/third_party/notices/libplacebo/LICENSE +458 -0
- package/third_party/notices/libxml2/Copyright +23 -0
- package/third_party/notices/libxml2/dict.c.notice +17 -0
- package/third_party/notices/libxml2/list.c.notice +16 -0
- package/third_party/notices/mpv/Copyright +78 -0
- package/third_party/notices/mpv/LICENSE.GPL +338 -0
- package/third_party/notices/mpv/LICENSE.LGPL +501 -0
- package/third_party/notices/vulkan-headers/LICENSE.txt +202 -0
- package/third_party/notices/zimg/COPYING +14 -0
- package/third_party/notices/zlib/LICENSE +22 -0
- package/third_party/notices.json +166 -0
- package/toolchain.lock.json +204 -0
- package/web/audio-worklet.js +45 -0
- package/web/cheap-mp4-probe.js +82 -0
- package/web/engine-adaptation/manifest.json +75 -0
- package/web/engine-adaptation/remux.mjs +2 -0
- package/web/engine-adaptation/remux.wasm +0 -0
- package/web/engine-ass/manifest.json +55 -0
- package/web/engine-ass/subtitles.mjs +2 -0
- package/web/engine-ass/subtitles.wasm +0 -0
- package/web/engine-hybrid/player.mjs +2 -0
- package/web/engine-hybrid/player.wasm +0 -0
- package/web/engine-remux/remux.mjs +2 -0
- package/web/engine-remux/remux.wasm +0 -0
- package/web/engine-software-full/player.mjs +2 -0
- package/web/engine-software-full/player.wasm +0 -0
- package/web/file-reader.js +34 -0
- package/web/filter-retained-engine-worker.js +292 -0
- package/web/generated/index.d.ts +6 -0
- package/web/generated/index.js +4 -0
- package/web/generated/internal/assets.d.ts +2 -0
- package/web/generated/internal/assets.js +7 -0
- package/web/generated/internal/backend.d.ts +27 -0
- package/web/generated/internal/backend.js +1 -0
- package/web/generated/internal/errors.d.ts +13 -0
- package/web/generated/internal/errors.js +54 -0
- package/web/generated/internal/native-ass.d.ts +38 -0
- package/web/generated/internal/native-ass.js +206 -0
- package/web/generated/internal/native-player.d.ts +95 -0
- package/web/generated/internal/native-player.js +502 -0
- package/web/generated/internal/playback-plans.d.ts +526 -0
- package/web/generated/internal/playback-plans.js +110 -0
- package/web/generated/internal/selection.d.ts +45 -0
- package/web/generated/internal/selection.js +45 -0
- package/web/generated/internal/state.d.ts +8 -0
- package/web/generated/internal/state.js +56 -0
- package/web/generated/internal/wasm-player.d.ts +127 -0
- package/web/generated/internal/wasm-player.js +372 -0
- package/web/generated/player/index.d.ts +198 -0
- package/web/generated/player/index.js +873 -0
- package/web/generated/player/interaction.d.ts +6 -0
- package/web/generated/player/interaction.js +14 -0
- package/web/generated/player/styles.d.ts +1 -0
- package/web/generated/player/styles.js +86 -0
- package/web/generated/types.d.ts +227 -0
- package/web/generated/types.js +2 -0
- package/web/generated/unified-player.d.ts +129 -0
- package/web/generated/unified-player.js +1074 -0
- package/web/io-worker.js +65 -0
- package/web/native-ass-worker.js +26 -0
- package/web/native-remux-player.js +301 -0
- package/web/native-remux-source-worker.js +25 -0
- package/web/native-remux-worker.js +46 -0
- package/web/range-reader.js +130 -0
- package/web/remux-packaging.js +11 -0
- package/web/resource-loader.js +173 -0
- package/web/retained-decoder-worker.js +200 -0
- package/web/retained-video.js +15 -0
- package/web/segmented-subtitles.js +37 -0
- package/web/software-full-engine-worker.js +234 -0
- package/web/source-probe.js +28 -0
- package/web/split-mp4.js +159 -0
- package/web/streaming-manifest.js +136 -0
- package/web/subtitle-overlay.js +26 -0
- package/web/video-codec-config.js +59 -0
- package/web/vod-manifest.js +54 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Codec-specific transport descriptions; capability acceptance still requires
|
|
2
|
+
// VideoDecoder.isConfigSupported AND successful decoded-frame delivery.
|
|
3
|
+
const hex=n=>n.toString(16).padStart(2,'0');
|
|
4
|
+
const pad=n=>String(n).padStart(2,'0');
|
|
5
|
+
// VP9 key-frame uncompressed header (WebM bitstream specification 6.2).
|
|
6
|
+
export function vp9PacketConfig(data){
|
|
7
|
+
let at=0;const bits=n=>{if(at+n>data.length*8)throw Error('Truncated VP9 header');let v=0;while(n--)v=v*2+((data[at>>3]>>(7-(at++&7)))&1);return v;};
|
|
8
|
+
if(bits(2)!==2)throw Error('Invalid VP9 frame marker');
|
|
9
|
+
const profile=bits(1)+2*bits(1);if(profile===3&&bits(1))throw Error('Invalid VP9 profile');
|
|
10
|
+
if(bits(1)||bits(1))throw Error('VP9 initialization requires a key frame');
|
|
11
|
+
bits(2);if(bits(24)!==0x498342)throw Error('Invalid VP9 sync code');
|
|
12
|
+
return {profile,depth:profile>=2?(bits(1)?12:10):8};
|
|
13
|
+
}
|
|
14
|
+
// Full key-frame color config is needed when packet-only demuxing leaves pix_fmt unset.
|
|
15
|
+
export function vp9RemuxConfig(data){
|
|
16
|
+
const basic=vp9PacketConfig(data);let at=0;
|
|
17
|
+
const bits=n=>{if(at+n>data.length*8)throw Error('Truncated VP9 color config');let v=0;while(n--)v=v*2+((data[at>>3]>>(7-(at++&7)))&1);return v;};
|
|
18
|
+
bits(2);bits(2);if(basic.profile===3)bits(1);bits(4);bits(24);if(basic.profile>=2)bits(1);
|
|
19
|
+
const color=bits(3);let fullRange=1,sx=0,sy=0;
|
|
20
|
+
if(color!==7){fullRange=bits(1);if(basic.profile===1||basic.profile===3){sx=bits(1);sy=bits(1);bits(1);}else{sx=sy=1;}}
|
|
21
|
+
else if(basic.profile===1||basic.profile===3)bits(1);else throw Error('Invalid VP9 RGB profile');
|
|
22
|
+
const base=color===7?'gbrp':sx?(sy?'yuv420p':'yuv422p'):sy?'yuv440p':'yuv444p';
|
|
23
|
+
return {...basic,pixelFormat:base+(basic.depth===8?'':basic.depth+'le'),fullRange};
|
|
24
|
+
}
|
|
25
|
+
function nal(data,type,hevc=false){
|
|
26
|
+
for(let i=0;i+4<data.length;i++)if(data[i]===0&&data[i+1]===0&&(data[i+2]===1||(data[i+2]===0&&data[i+3]===1))){
|
|
27
|
+
const start=i+(data[i+2]===1?3:4);if((hevc?(data[start]>>1)&63:data[start]&31)!==type)continue;
|
|
28
|
+
let end=start+1;while(end+3<data.length&&!(data[end]===0&&data[end+1]===0&&(data[end+2]===1||(data[end+2]===0&&data[end+3]===1))))end++;
|
|
29
|
+
if(end+3>=data.length)end=data.length;return data.subarray(start,end);
|
|
30
|
+
}return null;
|
|
31
|
+
}
|
|
32
|
+
function rbsp(data){const out=[];for(let i=0;i<data.length;i++){if(i>=2&&data[i]===3&&data[i-1]===0&&data[i-2]===0)continue;out.push(data[i]);}return Uint8Array.from(out);}
|
|
33
|
+
function hevcString(ptl){
|
|
34
|
+
if(ptl.length<12)throw Error('HEVC profile/tier/level unavailable');
|
|
35
|
+
let compatibility=0;for(let i=0;i<32;i++)if(ptl[1+(i>>3)]&(1<<(7-(i&7))))compatibility=(compatibility|(1<<i))>>>0;
|
|
36
|
+
const constraints=[...ptl.subarray(5,11)];while(constraints.length&&constraints.at(-1)===0)constraints.pop();
|
|
37
|
+
return `hev1.${['','A','B','C'][ptl[0]>>6]}${ptl[0]&31}.${compatibility.toString(16)}.${ptl[0]&32?'H':'L'}${ptl[11]}${constraints.map(x=>'.'+hex(x)).join('')}`;
|
|
38
|
+
}
|
|
39
|
+
export function videoCodecConfig({kind,description=new Uint8Array(),width,height,profile=-1,level=-1,depth=8,maxWidth=8192,maxHeight=8192}){
|
|
40
|
+
if(width<1||height<1||width>maxWidth||height>maxHeight||width*height>33554432||description.length>65536)throw Error('Video configuration exceeds current resource bounds');
|
|
41
|
+
let codec,extra,prefix;
|
|
42
|
+
if(kind===1){
|
|
43
|
+
if(description[0]===1&&description.length>=7){codec='avc1.'+[...description.subarray(1,4)].map(hex).join('');extra=description;}
|
|
44
|
+
else{const sps=nal(description,7);if(!sps||sps.length<4)throw Error('AVC SPS unavailable');codec='avc1.'+[...sps.subarray(1,4)].map(hex).join('');prefix=description;}
|
|
45
|
+
}else if(kind===2){
|
|
46
|
+
if(description[0]===1&&description.length>=23){codec=hevcString(description.subarray(1,13));extra=description;}
|
|
47
|
+
else{const sps=nal(description,33,true);if(!sps)throw Error('HEVC SPS unavailable');codec=hevcString(rbsp(sps).subarray(3,15));prefix=description;}
|
|
48
|
+
}else if(kind===3)codec='vp8';
|
|
49
|
+
else if(kind===4){
|
|
50
|
+
if(profile<0||profile>3||![8,10,12].includes(depth))throw Error('VP9 profile/bit depth unavailable');
|
|
51
|
+
codec=`vp09.${pad(profile)}.${pad([10,11,20,21,30,31,40,41,50,51,52,60,61,62].includes(level)?level:10)}.${pad(depth)}`;
|
|
52
|
+
}else if(kind===5){
|
|
53
|
+
let tier='M';
|
|
54
|
+
if(description.length>=4&&(description[0]&128)){profile=description[1]>>5;level=description[1]&31;tier=description[2]&128?'H':'M';depth=description[2]&64?(description[2]&32?12:10):8;prefix=description.subarray(4);}
|
|
55
|
+
if(profile<0||profile>2||level<0||level>23||![8,10,12].includes(depth))throw Error('AV1 profile/level/bit depth unavailable');
|
|
56
|
+
codec=`av01.${profile}.${pad(level)}${tier}.${pad(depth)}`;
|
|
57
|
+
}else throw Error('Video codec has no WebCodecs bridge');
|
|
58
|
+
return {configuration:{codec,...(extra?{description:extra}:{}),codedWidth:width,codedHeight:height,hardwareAcceleration:'no-preference',optimizeForLatency:false},prefix};
|
|
59
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Admission policy only. FFmpeg remains the manifest/timeline parser.
|
|
2
|
+
export function validateVODManifest(bytes,format,options={}){
|
|
3
|
+
const text=new TextDecoder('utf-8',{fatal:true}).decode(bytes).replace(/^\uFEFF/,'');
|
|
4
|
+
if(format==='hls'){
|
|
5
|
+
const lines=text.split(/\r?\n/).map(s=>s.trim());
|
|
6
|
+
if(lines[0]!=='#EXTM3U')throw Error('Invalid HLS manifest');
|
|
7
|
+
if(lines.some(s=>/^#EXT-X-(KEY|SESSION-KEY|PART|PRELOAD-HINT|SERVER-CONTROL|RENDITION-REPORT|SKIP):/.test(s)))throw Error('Encrypted or low-latency HLS is unsupported');
|
|
8
|
+
const variants=lines.filter(s=>s.startsWith('#EXT-X-STREAM-INF:')).length;
|
|
9
|
+
if(variants>1||lines.some(s=>s.startsWith('#EXT-X-I-FRAME-STREAM-INF:')))throw Error('Select one HLS variant before demuxing');
|
|
10
|
+
if(!options.live&&!variants&&!lines.includes('#EXT-X-ENDLIST'))throw Error('HLS must be finite VOD');
|
|
11
|
+
if(lines.filter(s=>s.startsWith('#EXTINF:')).length>10000)throw Error('HLS segment count limit exceeded');
|
|
12
|
+
const subtitlePlaylists=[];
|
|
13
|
+
for(const line of lines.filter(s=>s.startsWith('#EXT-X-MEDIA:')&&/(?:[:,])TYPE=SUBTITLES(?:,|$)/.test(s))){
|
|
14
|
+
const uri=/(?:[:,])URI="([^"]+)"(?:,|$)/.exec(line)?.[1];
|
|
15
|
+
if(!uri)throw Error('Invalid HLS subtitle URI');subtitlePlaylists.push(uri);
|
|
16
|
+
}
|
|
17
|
+
if(subtitlePlaylists.length>16)throw Error('HLS subtitle track limit exceeded');
|
|
18
|
+
return {subtitlePlaylists,segments:lines.filter(s=>s&&!s.startsWith('#')),segmentCount:lines.filter(s=>s.startsWith('#EXTINF:')).length};
|
|
19
|
+
}
|
|
20
|
+
if(format!=='dash')throw Error('Unknown segmented format');
|
|
21
|
+
// Accept a deliberately small XML spelling subset. Reject entities, prefixed
|
|
22
|
+
// element names and declarations before policy inspection; native libxml2
|
|
23
|
+
// performs the actual well-formedness and DASH parse.
|
|
24
|
+
if(/<!DOCTYPE|<!ENTITY|<!\[CDATA\[|&#/i.test(text))throw Error('Unsupported DASH XML declaration');
|
|
25
|
+
const clean=text.replace(/<!--[\s\S]*?-->/g,'').replace(/<\?xml[^?]*\?>/g,'');
|
|
26
|
+
const stack=[];let root=false,periods=0,adaptations=0,segments=0,hasTemplate=false,hasTimeline=false,hasDuration=false;
|
|
27
|
+
for(const match of clean.matchAll(/<([^>]+)>/g)){
|
|
28
|
+
const token=match[1];const closing=token.startsWith('/'),selfClosing=token.endsWith('/');
|
|
29
|
+
const name=/^\/?([A-Za-z][A-Za-z0-9]*)\b/.exec(token)?.[1];
|
|
30
|
+
if(!name||/^\/?[\w]+:/.test(token))throw Error('Unsupported DASH XML element');
|
|
31
|
+
if(closing){if(stack.pop()?.name!==name)throw Error('Malformed DASH XML');continue;}
|
|
32
|
+
if(!root){if(name!=='MPD'||!(options.live?/(?:^|\s)type\s*=\s*(['"])(?:static|dynamic)\1(?:\s|$)/:/(?:^|\s)type\s*=\s*(['"])static\1(?:\s|$)/).test(token))throw Error('DASH must be static VOD');root=true;}
|
|
33
|
+
if(['ContentProtection','Location','PatchLocation','EventStream'].includes(name)||/\bxlink:/.test(token))throw Error('Unsupported DASH extension');
|
|
34
|
+
if(name==='MPD'&&/\bmediaPresentationDuration\s*=\s*(['"])P[^'"]+\1/.test(token))hasDuration=true;
|
|
35
|
+
if(name==='Period'&&/\bduration\s*=\s*(['"])P[^'"]+\1/.test(token))hasDuration=true;
|
|
36
|
+
if(name==='SegmentTemplate')hasTemplate=true;
|
|
37
|
+
if(name==='SegmentTimeline')hasTimeline=true;
|
|
38
|
+
if(name==='SegmentURL'&&++segments>10000)throw Error('DASH segment count limit exceeded');
|
|
39
|
+
if(name==='S'){
|
|
40
|
+
const repeat=/(?:^|\s)r\s*=\s*(['"])([^'"]+)\1/.exec(token)?.[2]??'0';
|
|
41
|
+
if(!/^\d{1,5}$/.test(repeat))throw Error('Unbounded DASH timeline is unsupported');
|
|
42
|
+
segments+=Number(repeat)+1;if(segments>10000)throw Error('DASH segment count limit exceeded');
|
|
43
|
+
}
|
|
44
|
+
if(name==='Period'&&++periods>1)throw Error('Multiple DASH periods are unsupported');
|
|
45
|
+
if(name==='AdaptationSet'&&++adaptations>16)throw Error('DASH track limit exceeded');
|
|
46
|
+
if(name==='Representation'){
|
|
47
|
+
const parent=stack.at(-1);if(parent?.name!=='AdaptationSet'||++parent.representations>1)throw Error('Select one DASH representation before demuxing');
|
|
48
|
+
}
|
|
49
|
+
if(name==='S'&&/(?:^|\s)r\s*=\s*(['"])-/.test(token))throw Error('Unbounded DASH timeline is unsupported');
|
|
50
|
+
if(!selfClosing)stack.push({name,representations:0});
|
|
51
|
+
}
|
|
52
|
+
if(!options.live&&hasTemplate&&!hasTimeline&&!hasDuration)throw Error('DASH template requires a finite duration or timeline');
|
|
53
|
+
if(!root||stack.length||periods!==1)throw Error('Malformed DASH VOD manifest');
|
|
54
|
+
}
|