mirror of
https://github.com/hexahigh/080609.git
synced 2025-12-11 19:55:06 +01:00
feat: Added browser codec support checker
This commit is contained in:
parent
e71323f02f
commit
8308e02d1f
@ -36,6 +36,7 @@
|
||||
"flowbite": "^3.1.2",
|
||||
"flowbite-svelte": "^0.47.4",
|
||||
"fuse.js": "^7.1.0",
|
||||
"iconify-icon": "^2.3.0",
|
||||
"mode-watcher": "^0.5.0",
|
||||
"msgpackr": "^1.11.2",
|
||||
"pako": "^2.1.0",
|
||||
|
||||
15
pnpm-lock.yaml
generated
15
pnpm-lock.yaml
generated
@ -26,6 +26,9 @@ importers:
|
||||
fuse.js:
|
||||
specifier: ^7.1.0
|
||||
version: 7.1.0
|
||||
iconify-icon:
|
||||
specifier: ^2.3.0
|
||||
version: 2.3.0
|
||||
mode-watcher:
|
||||
specifier: ^0.5.0
|
||||
version: 0.5.0(svelte@5.2.11)
|
||||
@ -273,6 +276,9 @@ packages:
|
||||
'@floating-ui/utils@0.2.9':
|
||||
resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==}
|
||||
|
||||
'@iconify/types@2.0.0':
|
||||
resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
|
||||
|
||||
'@isaacs/cliui@8.0.2':
|
||||
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
|
||||
engines: {node: '>=12'}
|
||||
@ -852,6 +858,9 @@ packages:
|
||||
html-void-elements@3.0.0:
|
||||
resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
|
||||
|
||||
iconify-icon@2.3.0:
|
||||
resolution: {integrity: sha512-C0beI9oTDxQz6voI5CKl7MiJf0Lw4UU8K4G4t6pcUDClLmCvuMOpcvd8MAztQ2SfoH0iv7WHdxBFjekKPFKH2Q==}
|
||||
|
||||
import-meta-resolve@4.1.0:
|
||||
resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==}
|
||||
|
||||
@ -1724,6 +1733,8 @@ snapshots:
|
||||
|
||||
'@floating-ui/utils@0.2.9': {}
|
||||
|
||||
'@iconify/types@2.0.0': {}
|
||||
|
||||
'@isaacs/cliui@8.0.2':
|
||||
dependencies:
|
||||
string-width: 5.1.2
|
||||
@ -2290,6 +2301,10 @@ snapshots:
|
||||
|
||||
html-void-elements@3.0.0: {}
|
||||
|
||||
iconify-icon@2.3.0:
|
||||
dependencies:
|
||||
'@iconify/types': 2.0.0
|
||||
|
||||
import-meta-resolve@4.1.0: {}
|
||||
|
||||
is-binary-path@2.1.0:
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import 'iconify-icon';
|
||||
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
@ -17,7 +17,21 @@
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import { unified } from 'unified';
|
||||
|
||||
let alerts = [];
|
||||
let alerts: {
|
||||
text: string;
|
||||
color:
|
||||
| 'dark'
|
||||
| 'red'
|
||||
| 'yellow'
|
||||
| 'green'
|
||||
| 'indigo'
|
||||
| 'purple'
|
||||
| 'pink'
|
||||
| 'blue'
|
||||
| 'primary'
|
||||
| 'none';
|
||||
icon: string;
|
||||
}[] = $state([]);
|
||||
|
||||
let conversionOptions = $state({
|
||||
gfm: true,
|
||||
@ -39,14 +53,15 @@
|
||||
|
||||
outputValue = u.processSync(inputValue).toString();
|
||||
|
||||
alerts.push({ text: 'Conversion successful', color: 'green', icon: 'nrk:check' });
|
||||
} catch (e) {
|
||||
outputValue = e
|
||||
alerts.push({ text: e, color: 'red' });
|
||||
outputValue = e;
|
||||
alerts.push({ text: e, color: 'red', icon: 'nrk:close' });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex min-h-screen items-center justify-between gap-4 px-4">
|
||||
<div class="flex min-h-screen flex-col items-center justify-between gap-4 px-4 md:flex-row">
|
||||
<!-- Left Section -->
|
||||
<div class="flex flex-1 flex-col items-center justify-center space-y-2">
|
||||
<Label for="markdown-input">Markdown</Label>
|
||||
@ -85,9 +100,21 @@
|
||||
bind:value={outputValue}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="toast-container">
|
||||
{#each alerts as alert}
|
||||
<Toast transition={fly} params={{ x: 200 }} color={alert.color} class="mb-4">
|
||||
<iconify-icon icon={alert.icon} slot="icon"></iconify-icon>
|
||||
{alert.text}
|
||||
</Toast>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.toast-container {
|
||||
position: fixed;
|
||||
right: 1rem;
|
||||
bottom: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
211
src/routes/(normal)/supports/+page.svelte
Normal file
211
src/routes/(normal)/supports/+page.svelte
Normal file
@ -0,0 +1,211 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { getAllAV1Codecs, getAllAVCCodecs, getAllHEVCCodecs, getAllVP9Codecs } from './codecs';
|
||||
|
||||
type ResultType = {
|
||||
name: string;
|
||||
// The result type. If it is a boolean it will be rendered as a tick or cross. If it is a number it will be a percentage of the 'total' value.
|
||||
type?: 'boolean' | 'string' | 'number';
|
||||
supported: boolean | string | number;
|
||||
// Has no effect if the result type is not a number
|
||||
total?: number;
|
||||
};
|
||||
|
||||
type SectionType = {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
note: string;
|
||||
results?: ResultType[];
|
||||
};
|
||||
|
||||
class Results {
|
||||
sections: SectionType[] = [];
|
||||
addSection(id: string, name: string, description: string, note?: string) {
|
||||
this.sections.push({
|
||||
id: id,
|
||||
name: name,
|
||||
description: description,
|
||||
note: note,
|
||||
results: []
|
||||
});
|
||||
}
|
||||
addFormat(sectionId: string, resultData: ResultType) {
|
||||
// Find the section
|
||||
const section = this.sections.find((section) => section.id === sectionId);
|
||||
if (section) {
|
||||
// Add the result
|
||||
section.results.push(resultData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let results = new Results();
|
||||
let isChecking = true;
|
||||
|
||||
const imageFormats: { name: string; mime: string }[] = [
|
||||
{ name: 'AVIF', mime: 'avif' },
|
||||
{ name: 'WEBP', mime: 'webp' },
|
||||
{ name: 'JPEG', mime: 'jpeg' },
|
||||
{ name: 'PNG', mime: 'png' },
|
||||
{ name: 'GIF', mime: 'gif' },
|
||||
{ name: 'BMP', mime: 'bmp' },
|
||||
{ name: 'ICO', mime: 'vnd.microsoft.icon' },
|
||||
{ name: 'SVG', mime: 'svg+xml' },
|
||||
{ name: 'HEIC', mime: 'heic' },
|
||||
{ name: 'HEIF', mime: 'heif' },
|
||||
{ name: 'TIFF', mime: 'tiff' },
|
||||
{ name: 'JPEG XL', mime: 'jxl' }
|
||||
];
|
||||
|
||||
async function checkImageFormat(format: string): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
const img = new Image();
|
||||
img.src = `data:image/${format};base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=`;
|
||||
img.onload = () => resolve(true);
|
||||
img.onerror = () => resolve(false);
|
||||
});
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
// Check image formats
|
||||
const imageResults = await Promise.all(
|
||||
imageFormats.map(async (format) => checkImageFormat(format.mime))
|
||||
);
|
||||
results.addSection(
|
||||
'image',
|
||||
'Image Formats',
|
||||
'Image formats detected using data URIs',
|
||||
'This tends to be pretty inaccurate'
|
||||
);
|
||||
imageFormats.forEach((format, i) => {
|
||||
results.addFormat('image', {
|
||||
name: format.name,
|
||||
type: 'boolean',
|
||||
supported: imageResults[i]
|
||||
});
|
||||
});
|
||||
|
||||
results.addSection('video.codecs', 'Video Codecs', 'Video codecs detected using MediaSource.isTypeSupported(). The larger the number displayed the more variations of the codec are supported.');
|
||||
const av1Codecs = getAllAV1Codecs();
|
||||
const avcCodecs = getAllAVCCodecs();
|
||||
const hevcCodecs = getAllHEVCCodecs();
|
||||
const vp9Codecs = getAllVP9Codecs();
|
||||
let av1CodecsSupported = 0;
|
||||
let avcCodecsSupported = 0;
|
||||
let hevcCodecsSupported = 0;
|
||||
let vp9CodecsSupported = 0;
|
||||
|
||||
av1Codecs.forEach((codec) => {
|
||||
const supported = MediaSource.isTypeSupported(`video/mp4; codecs="${codec.codec}"`);
|
||||
if (supported) {
|
||||
av1CodecsSupported++;
|
||||
}
|
||||
});
|
||||
|
||||
avcCodecs.forEach((codec) => {
|
||||
const supported = MediaSource.isTypeSupported(`video/mp4; codecs="${codec.codec}"`);
|
||||
if (supported) {
|
||||
avcCodecsSupported++;
|
||||
}
|
||||
});
|
||||
|
||||
hevcCodecs.forEach((codec) => {
|
||||
const supported = MediaSource.isTypeSupported(`video/mp4; codecs="${codec.codec}"`);
|
||||
if (supported) {
|
||||
hevcCodecsSupported++;
|
||||
}
|
||||
});
|
||||
|
||||
vp9Codecs.forEach((codec) => {
|
||||
const supported = MediaSource.isTypeSupported(`video/mp4; codecs="${codec.codec}"`);
|
||||
if (supported) {
|
||||
vp9CodecsSupported++;
|
||||
}
|
||||
});
|
||||
|
||||
results.addFormat('video.codecs', {
|
||||
name: 'AV1',
|
||||
type: 'number',
|
||||
supported: av1CodecsSupported,
|
||||
total: av1Codecs.length
|
||||
});
|
||||
results.addFormat('video.codecs', {
|
||||
name: 'AVC',
|
||||
type: 'number',
|
||||
supported: avcCodecsSupported,
|
||||
total: avcCodecs.length
|
||||
});
|
||||
results.addFormat('video.codecs', {
|
||||
name: 'HEVC',
|
||||
type: 'number',
|
||||
supported: hevcCodecsSupported,
|
||||
total: hevcCodecs.length
|
||||
});
|
||||
results.addFormat('video.codecs', {
|
||||
name: 'VP9',
|
||||
type: 'number',
|
||||
supported: vp9CodecsSupported,
|
||||
total: vp9Codecs.length
|
||||
});
|
||||
|
||||
isChecking = false;
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="min-h-screen bg-gray-100 px-4 py-8">
|
||||
<div class="mx-auto max-w-2xl space-y-6">
|
||||
<h1 class="text-center text-3xl font-bold text-gray-900">Browser Format Support Checker</h1>
|
||||
|
||||
{#if isChecking}
|
||||
<div class="text-center text-gray-600">Checking supported formats...</div>
|
||||
{:else}
|
||||
{#each results.sections as section}
|
||||
<div class="rounded-lg bg-white p-6 shadow">
|
||||
<h2 class="mb-4 text-xl font-semibold">{section.name}</h2>
|
||||
<p class="mb-2 text-center text-gray-600">{section.description}</p>
|
||||
{#if section.note}
|
||||
<p class="mb-2 text-center text-yellow-600">{section.note}</p>
|
||||
{/if}
|
||||
<ul class="space-y-2">
|
||||
{#each section.results as format}
|
||||
{#if format.type === 'boolean'}
|
||||
<li
|
||||
class="flex items-center justify-between rounded-lg p-3 {format.supported
|
||||
? 'bg-green-50'
|
||||
: 'bg-red-50'}"
|
||||
>
|
||||
<span class="font-medium">{format.name}</span>
|
||||
<div class="flex items-center space-x-2">
|
||||
<div
|
||||
class={`h-2 w-2 rounded-full ${format.supported ? 'bg-green-500' : 'bg-red-500'}`}
|
||||
/>
|
||||
<span class={format.supported ? 'text-green-700' : 'text-red-700'}>
|
||||
{format.supported ? 'Supported' : 'Not Supported'}
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
{:else if format.type === 'number' && typeof format.supported === 'number'}
|
||||
<li
|
||||
class="flex items-center justify-between rounded-lg p-3"
|
||||
style="background-color: hsl({(format.supported / format.total) * 120}, 100%, 90%)"
|
||||
>
|
||||
<span class="font-medium">{format.name}</span>
|
||||
<div class="flex items-center space-x-2">
|
||||
<div
|
||||
style="background-color: hsl({(format.supported / format.total) * 120}, 100%, 50%)"
|
||||
class="h-2 w-2 rounded-full"
|
||||
/>
|
||||
<span class="text-gray-700">
|
||||
{format.supported}/{format.total}
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
{/if}
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
283
src/routes/(normal)/supports/codecs.js
Normal file
283
src/routes/(normal)/supports/codecs.js
Normal file
@ -0,0 +1,283 @@
|
||||
export function getAllAVCCodecs()
|
||||
{
|
||||
var AVC_PROFILES_DESC = [
|
||||
//{ constrained_set0_flag: true },
|
||||
//{ constrained_set1_flag: true },
|
||||
//{ constrained_set2_flag: true },
|
||||
{ profile_idc: 66, description: "Baseline" },
|
||||
{ profile_idc: 66, description: "Constrained Baseline", constrained_set1_flag: true},
|
||||
{ profile_idc: 77, description: "Main" },
|
||||
{ profile_idc: 77, description: "Constrained Main", constrained_set1_flag: true},
|
||||
{ profile_idc: 88, description: "Extended" },
|
||||
{ profile_idc: 100, description: "High", constrained_set4_flag: false },
|
||||
{ profile_idc: 100, description: "High Progressive", constrained_set4_flag: true },
|
||||
{ profile_idc: 100, description: "Constrained High", constrained_set4_flag: true, constrained_set5_flag: true },
|
||||
{ profile_idc: 110, description: "High 10" },
|
||||
{ profile_idc: 110, description: "High 10 Intra", constrained_set3_flag: true },
|
||||
{ profile_idc: 122, description: "High 4:2:2" },
|
||||
{ profile_idc: 122, description: "High 4:2:2 Intra", constrained_set3_flag: true },
|
||||
{ profile_idc: 244, description: "High 4:4:4 Predictive" },
|
||||
{ profile_idc: 244, description: "High 4:4:4 Intra", constrained_set3_flag: true },
|
||||
{ profile_idc: 44, description: "CAVLC 4:4:4 Intra" }
|
||||
];
|
||||
|
||||
var AVC_PROFILES_IDC = [ 66, 77, 88, 100, 110, 122, 244, 44];
|
||||
var AVC_CONSTRAINTS = [ 0, 4, 8, 16, 32, 64, 128 ];
|
||||
var AVC_LEVELS = [ 10, 11, 12, 13, 20, 21, 22, 30, 31, 32, 40, 41, 42, 50, 51, 52];
|
||||
|
||||
var sj, sk, sl;
|
||||
var mimes = [];
|
||||
for (var j in AVC_PROFILES_IDC) {
|
||||
sj = AVC_PROFILES_IDC[j].toString(16);
|
||||
if (sj.length == 1) sj = "0"+sj;
|
||||
for (var k in AVC_CONSTRAINTS) {
|
||||
sk = AVC_CONSTRAINTS[k].toString(16);
|
||||
if (sk.length == 1) sk = "0"+sk;
|
||||
|
||||
var desc = "";
|
||||
for (let i in AVC_PROFILES_DESC) {
|
||||
if (AVC_PROFILES_IDC[j] == AVC_PROFILES_DESC[i].profile_idc) {
|
||||
var c = ((AVC_PROFILES_DESC[i].constrained_set0_flag ? 1 : 0) << 7) |
|
||||
((AVC_PROFILES_DESC[i].constrained_set1_flag ? 1 : 0) << 6) |
|
||||
((AVC_PROFILES_DESC[i].constrained_set2_flag ? 1 : 0) << 5) |
|
||||
((AVC_PROFILES_DESC[i].constrained_set3_flag ? 1 : 0) << 4) |
|
||||
((AVC_PROFILES_DESC[i].constrained_set4_flag ? 1 : 0) << 3) |
|
||||
((AVC_PROFILES_DESC[i].constrained_set5_flag ? 1 : 0) << 2);
|
||||
if (c === AVC_CONSTRAINTS[k]) {
|
||||
desc = AVC_PROFILES_DESC[i].description;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (desc.length > 0) {
|
||||
for (var l in AVC_LEVELS) {
|
||||
sl = AVC_LEVELS[l].toString(16);
|
||||
if (sl.length == 1) sl = "0"+sl;
|
||||
mimes.push({
|
||||
codec: 'avc1.'+sj+sk+sl,
|
||||
description: "AVC "+desc+" Level "+ AVC_LEVELS[l]/10
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return mimes;
|
||||
}
|
||||
|
||||
export function getAllAV1Codecs()
|
||||
{
|
||||
var PROFILES_VALUES = [ 0, 1, 2 ];
|
||||
var PROFILES_NAMES = [ 'Main', 'High', 'Professional' ];
|
||||
var LEVEL_VALUES = [ 0, 1, 2, 3,
|
||||
4, 5, 6, 7,
|
||||
8, 9, 10, 11,
|
||||
12, 13, 14, 15,
|
||||
16, 17, 18, 19,
|
||||
20, 21, 22, 23,
|
||||
31];
|
||||
var LEVEL_NAMES = [ '2.0', '2.1', '2.2', '2.3',
|
||||
'3.0', '3.1', '3.2', '3.3',
|
||||
'4.0', '4.1', '4.2', '4.3',
|
||||
'5.0', '5.1', '5.2', '5.3',
|
||||
'6.0', '6.1', '6.2', '6.3',
|
||||
'7.0', '6.1', '7.2', '7.3',
|
||||
'Max' ];
|
||||
var TIER_VALUES = [ 'M', 'H' ];
|
||||
var TIER_NAMES = [ 'Main', 'High' ];
|
||||
var DEPTH_VALUES = [ 8, 10, 12];
|
||||
var MONOCHROME_VALUES = [ null ];//, 0, 1 ];
|
||||
var CHROMA_SUBSAMPLING_VALUES = [ '000', '001', '010', '011', '100', '101', '110', '111' ];
|
||||
var COLOR_PRIMARIES_VALUES = [ 0, 1, 2];//, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ];
|
||||
var TRANSER_CHARACTERISTICS_VALUES = [ 0, 1, 2];//, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ];
|
||||
var MATRIX_COEFFICIENT_VALUES = [ 0, 1, 2];//, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ];
|
||||
var VIDEO_FULL_RANGE_FLAG_VALUES = [ 0, 1 ];
|
||||
|
||||
var allValues = [];
|
||||
for (var profile in PROFILES_VALUES) {
|
||||
for (var level in LEVEL_VALUES) {
|
||||
var levelString = ''+LEVEL_VALUES[level];
|
||||
if (levelString.length == 1) levelString = "0"+levelString;
|
||||
|
||||
for (var tier in TIER_VALUES) {
|
||||
for (var depth in DEPTH_VALUES) {
|
||||
var depthString = ''+DEPTH_VALUES[depth];
|
||||
if (depthString.length == 1) depthString = "0"+depthString;
|
||||
for (var mono in MONOCHROME_VALUES) {
|
||||
if (MONOCHROME_VALUES[mono]!= null) {
|
||||
for (var chroma in CHROMA_SUBSAMPLING_VALUES) {
|
||||
for (var colorPrimary in COLOR_PRIMARIES_VALUES) {
|
||||
for (var transfer in TRANSER_CHARACTERISTICS_VALUES) {
|
||||
for (var matrix in MATRIX_COEFFICIENT_VALUES) {
|
||||
for (var range in VIDEO_FULL_RANGE_FLAG_VALUES) {
|
||||
allValues.push({
|
||||
codec: 'av01.'+PROFILES_VALUES[profile]+
|
||||
'.'+levelString+
|
||||
''+TIER_VALUES[tier]+
|
||||
'.'+depthString+
|
||||
'.'+MONOCHROME_VALUES[mono]+
|
||||
'.'+CHROMA_SUBSAMPLING_VALUES[chroma]+
|
||||
'.'+COLOR_PRIMARIES_VALUES[colorPrimary]+
|
||||
'.'+TRANSER_CHARACTERISTICS_VALUES[transfer]+
|
||||
'.'+MATRIX_COEFFICIENT_VALUES[matrix]+
|
||||
'.'+VIDEO_FULL_RANGE_FLAG_VALUES[range],
|
||||
description: ''
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
allValues.push({
|
||||
codec: 'av01.'+PROFILES_VALUES[profile]+
|
||||
'.'+levelString+
|
||||
''+TIER_VALUES[tier]+
|
||||
'.'+depthString,
|
||||
description: 'AV1 '+PROFILES_NAMES[profile]+' Profile, level '+LEVEL_NAMES[level]+', '+TIER_NAMES[tier]+ ' tier, '+DEPTH_VALUES[depth]+' bits'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return allValues;
|
||||
}
|
||||
|
||||
export function getAllHEVCCodecs() {
|
||||
const HEVC_PROFILES_DESC = [
|
||||
{ profile_idc: 1, description: "Main" },
|
||||
{ profile_idc: 2, description: "Main 10" },
|
||||
{ profile_idc: 3, description: "Main Still Picture" },
|
||||
{ profile_idc: 4, description: "Range Extensions" },
|
||||
{ profile_idc: 5, description: "High Throughput" },
|
||||
{ profile_idc: 6, description: "Multiview Main" },
|
||||
{ profile_idc: 7, description: "Scalable Main" },
|
||||
{ profile_idc: 8, description: "3D Main" },
|
||||
{ profile_idc: 9, description: "Screen Content Coding Extensions" },
|
||||
{ profile_idc: 10, description: "Scalable Format Range Extensions" },
|
||||
];
|
||||
|
||||
const HEVC_TIERS = [
|
||||
{ tier: 0, tier_name: 'Main' },
|
||||
{ tier: 1, tier_name: 'High' }
|
||||
];
|
||||
|
||||
const HEVC_LEVELS = [30, 60, 63, 90, 93, 120, 123, 150, 153, 156, 180, 183, 186];
|
||||
const HEVC_CONSTRAINTS = ['B0']; // Placeholder for constraints
|
||||
|
||||
const mimes = [];
|
||||
for (const profile of HEVC_PROFILES_DESC) {
|
||||
for (const tier of HEVC_TIERS) {
|
||||
for (const level of HEVC_LEVELS) {
|
||||
for (const constraint of HEVC_CONSTRAINTS) {
|
||||
const tierCompatibility = tier.tier === 0 ? 6 : 4; // Example-based
|
||||
const levelHex = level.toString(16).toUpperCase().padStart(2, '0');
|
||||
const codec = `hev1.${profile.profile_idc}.${tierCompatibility}.L${levelHex}.${constraint}`;
|
||||
const levelName = (level / 30).toFixed(1);
|
||||
const description = `HEVC ${profile.description}, ${tier.tier_name} Tier, Level ${levelName}`;
|
||||
mimes.push({ codec, description });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return mimes;
|
||||
}
|
||||
|
||||
export function getAllVP9Codecs() {
|
||||
const VP9_PROFILES = [0, 1, 2, 3];
|
||||
const VP9_PROFILE_NAMES = ['Profile 0', 'Profile 1', 'Profile 2', 'Profile 3'];
|
||||
const VP9_LEVELS = [10, 11, 20, 21, 30, 31, 40, 41, 50, 51];
|
||||
const VP9_LEVEL_NAMES = {
|
||||
10: '1.0', 11: '1.1', 20: '2.0', 21: '2.1',
|
||||
30: '3.0', 31: '3.1', 40: '4.0', 41: '4.1',
|
||||
50: '5.0', 51: '5.1'
|
||||
};
|
||||
const VP9_BIT_DEPTHS = [8, 10, 12];
|
||||
const VP9_CHROMA_SUBSAMPLING = [0, 1, 2];
|
||||
const VP9_COLOR_PRIMARIES = [1, 9]; // 1=BT.709, 9=BT.2020
|
||||
const VP9_TRANSFER_CHARACTERISTICS = [1, 15]; // 1=BT.709, 15=HLG
|
||||
const VP9_MATRIX_COEFFICIENTS = [1, 9]; // 1=BT.709, 9=BT.2020
|
||||
const VP9_VIDEO_FULL_RANGE_FLAG = [0, 1];
|
||||
|
||||
const mimes = [];
|
||||
|
||||
const pad = (n) => n.toString().padStart(2, '0');
|
||||
|
||||
const getChromaName = (cs) => {
|
||||
switch (cs) {
|
||||
case 0: return '4:2:0';
|
||||
case 1: return '4:2:2';
|
||||
case 2: return '4:4:4';
|
||||
default: return 'Unknown';
|
||||
}
|
||||
};
|
||||
|
||||
const getColorPrimaryName = (cp) => {
|
||||
switch (cp) {
|
||||
case 1: return 'BT.709';
|
||||
case 9: return 'BT.2020';
|
||||
default: return 'Unknown';
|
||||
}
|
||||
};
|
||||
|
||||
const getTransferName = (tc) => {
|
||||
switch (tc) {
|
||||
case 1: return 'BT.709';
|
||||
case 15: return 'HLG';
|
||||
default: return 'Unknown';
|
||||
}
|
||||
};
|
||||
|
||||
const getMatrixName = (mc) => {
|
||||
switch (mc) {
|
||||
case 1: return 'BT.709';
|
||||
case 9: return 'BT.2020';
|
||||
default: return 'Unknown';
|
||||
}
|
||||
};
|
||||
|
||||
for (const profile of VP9_PROFILES) {
|
||||
for (const level of VP9_LEVELS) {
|
||||
const levelName = VP9_LEVEL_NAMES[level] || 'Unknown';
|
||||
for (const bitDepth of VP9_BIT_DEPTHS) {
|
||||
// Minimal codec string
|
||||
const minimalCodec = `vp09.${pad(profile)}.${pad(level)}.${pad(bitDepth)}`;
|
||||
const minimalDesc = `VP9 ${VP9_PROFILE_NAMES[profile]}, Level ${levelName}, ${bitDepth}-bit`;
|
||||
mimes.push({ codec: minimalCodec, description: minimalDesc });
|
||||
|
||||
// Full codec strings with all parameters
|
||||
for (const chroma of VP9_CHROMA_SUBSAMPLING) {
|
||||
for (const colorPrimary of VP9_COLOR_PRIMARIES) {
|
||||
for (const transfer of VP9_TRANSFER_CHARACTERISTICS) {
|
||||
for (const matrix of VP9_MATRIX_COEFFICIENTS) {
|
||||
for (const range of VP9_VIDEO_FULL_RANGE_FLAG) {
|
||||
const codec = [
|
||||
`vp09.${pad(profile)}`,
|
||||
pad(level),
|
||||
pad(bitDepth),
|
||||
pad(chroma),
|
||||
pad(colorPrimary),
|
||||
pad(transfer),
|
||||
pad(matrix),
|
||||
pad(range)
|
||||
].join('.');
|
||||
const desc = [
|
||||
minimalDesc,
|
||||
`Chroma ${getChromaName(chroma)}`,
|
||||
`Color ${getColorPrimaryName(colorPrimary)}`,
|
||||
`Transfer ${getTransferName(transfer)}`,
|
||||
`Matrix ${getMatrixName(matrix)}`,
|
||||
`${range ? 'Full' : 'Limited'} Range`
|
||||
].join(', ');
|
||||
mimes.push({ codec, description: desc });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return mimes;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user