mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-09-27 19:03:46 +02:00
Merge main into release for tag v1.9.1
This commit is contained in:
@@ -28,6 +28,14 @@ Mediabunny is a JavaScript library for reading, writing, and converting media fi
|
||||
</a>
|
||||
</div>
|
||||
|
||||
### Bronze sponsors
|
||||
|
||||
<div align="center">
|
||||
<a href="https://www.reactvideoeditor.com/" target="_blank">
|
||||
<img src="./docs/public/sponsors/rve.svg" width="40" height="40" alt="React Video Editor">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
[Sponsor Mediabunny's development](https://github.com/sponsors/Vanilagy)
|
||||
|
||||
## Features
|
||||
|
||||
+1
-2
@@ -19,9 +19,8 @@
|
||||
const videoTrack = await input.getPrimaryVideoTrack();
|
||||
const sink = new Mediabunny.EncodedPacketSink(videoTrack);
|
||||
|
||||
for await (const packet of sink.packets(undefined, undefined, { verifyKeyPackets: false })) {
|
||||
for await (const packet of sink.packets()) {
|
||||
console.log(packet);
|
||||
if (packet.timestamp >= 2.4) break;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+13
-1
@@ -92,6 +92,9 @@ const sponsors = {
|
||||
{ image: '/sponsors/diffusionstudio.png', name: 'Diffusion Studio', url: 'https://diffusion.studio/' },
|
||||
{ image: '/sponsors/kino.jpg', name: 'Kino', url: 'https://kino.ai/' },
|
||||
],
|
||||
bronze: [
|
||||
{ image: '/sponsors/rve.svg', name: 'React Video Editor', url: 'https://www.reactvideoeditor.com/' },
|
||||
],
|
||||
individual: [
|
||||
{ image: 'https://avatars.githubusercontent.com/u/84167135', name: 'Memenome', url: 'https://github.com/memenome' },
|
||||
{ image: 'https://avatars.githubusercontent.com/u/5913254', name: 'Brandon McConnell', url: 'https://github.com/brandonmcconnell' },
|
||||
@@ -336,8 +339,17 @@ await conversion.execute();
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="sponsors.bronze.length > 0">
|
||||
<h3 class="!text-xl">Bronze sponsors</h3>
|
||||
<div class="flex flex-wrap mt-1 justify-center gap-1">
|
||||
<a v-for="sponsor in sponsors.bronze" :href="sponsor.url" target="_blank" class="flex items-center p-2 rounded-full hover:bg-(--vp-c-gray-3) !text-(--vp-c-text-1) !no-underline">
|
||||
<img :src="sponsor.image" class="size-10 rounded-full">
|
||||
<p class="!my-0 !font-medium px-3 text-sm">{{ sponsor.name }}</p>
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="sponsors.individual.length > 0">
|
||||
<h4>Individual sponsors</h4>
|
||||
<h4 class="!text-base">Individual sponsors</h4>
|
||||
<div class="flex flex-wrap mt-1 justify-center">
|
||||
<a v-for="sponsor in sponsors.individual" :href="sponsor.url" target="_blank" class="flex gap-1 w-24 flex-col items-center p-2 rounded-xl hover:bg-(--vp-c-gray-3) !text-(--vp-c-text-1) !no-underline">
|
||||
<img :src="sponsor.image" class="size-8 rounded-full">
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg width="52" height="51" viewBox="0 0 52 51" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M25.1186 1.50109C23.3805 0.147773 21.1954 -0.253724 19.1203 0.397437L5.08373 4.80289C2.19204 5.71105 0.248047 8.44236 0.248047 11.6022V42.9894C0.248047 45.2558 1.24465 47.3313 2.98266 48.6825C4.72099 50.036 6.9084 50.4373 8.98111 49.7863L19.4595 46.4967C19.1318 45.366 18.9658 44.1862 18.9658 42.9894V28.735C18.9658 25.7417 20.0093 22.8829 21.9088 20.6899C23.303 19.0744 25.1116 17.8826 27.1329 17.2463L27.8534 17.0309V7.19654C27.8534 4.93006 26.8568 2.85458 25.1185 1.50109H25.1186Z" fill="#1C2AC5"/>
|
||||
<path d="M51.2479 24.3293V38.5836C51.2479 41.7411 49.3061 44.4749 46.4122 45.3806L32.3756 49.7862C31.7253 49.9917 31.0632 50.0922 30.4081 50.0922C28.9739 50.0922 27.5703 49.6125 26.3771 48.6824C25.832 48.2565 25.3595 47.7645 24.9711 47.2139C24.4986 46.553 24.1453 45.8164 23.923 45.0257C23.9206 45.0257 23.9184 45.0257 23.916 45.0281L23.9206 45.0232C23.7406 44.3746 23.6423 43.6917 23.6423 42.9893V28.7349C23.6423 27.2712 24.0588 25.8983 24.7935 24.7576C25.528 23.6169 26.5807 22.7066 27.8534 22.1656C28.057 22.08 28.2652 22.0015 28.4782 21.9356L30.1953 21.3971L42.515 17.53C44.5899 16.8788 46.7751 17.2803 48.5132 18.6338C50.2516 19.9873 51.248 22.0628 51.248 24.3293H51.2479Z" fill="#3E8AF5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
Generated
+6
-6
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "mediabunny",
|
||||
"version": "1.9.0",
|
||||
"version": "1.9.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mediabunny",
|
||||
"version": "1.9.0",
|
||||
"version": "1.9.1",
|
||||
"license": "MPL-2.0",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
@@ -5900,9 +5900,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mediabunny": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.8.0.tgz",
|
||||
"integrity": "sha512-+JjE+A2NxtjxnBsEy4yCw9CYx8yDyJzivAx28xU5vWqEy4Xu9gE8qiIklKWfLEAoiyudJ+wn0gn3rl1HtK8vcw==",
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.9.0.tgz",
|
||||
"integrity": "sha512-pNviaYemNRjrIbx1K1+Q1xcCbD1PRVp6i1+0GXJf5NPVeHUVJfJY2xkhtBjbd4LpbqCTqRoSPYxcrOmIRt14bw==",
|
||||
"license": "MPL-2.0",
|
||||
"peer": true,
|
||||
"workspaces": [
|
||||
@@ -9017,7 +9017,7 @@
|
||||
},
|
||||
"packages/mp3-encoder": {
|
||||
"name": "@mediabunny/mp3-encoder",
|
||||
"version": "1.9.0",
|
||||
"version": "1.9.1",
|
||||
"license": "MPL-2.0",
|
||||
"devDependencies": {
|
||||
"@types/emscripten": "^1.40.1"
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "mediabunny",
|
||||
"author": "Vanilagy",
|
||||
"version": "1.9.0",
|
||||
"version": "1.9.1",
|
||||
"description": "Pure TypeScript media toolkit for reading, writing, and converting media files, directly in the browser.",
|
||||
"type": "module",
|
||||
"workspaces": [
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@mediabunny/mp3-encoder",
|
||||
"author": "Vanilagy",
|
||||
"version": "1.9.0",
|
||||
"version": "1.9.1",
|
||||
"description": "MP3 encoder extension for Mediabunny, based on LAME.",
|
||||
"main": "./dist/bundles/mediabunny-mp3-encoder.mjs",
|
||||
"module": "./dist/bundles/mediabunny-mp3-encoder.mjs",
|
||||
|
||||
+23
-2
@@ -122,9 +122,8 @@ export const LEVEL_0_EBML_IDS: EBMLId[] = [
|
||||
EBMLId.Segment,
|
||||
];
|
||||
|
||||
// All the stuff that can appear in a segment, basically
|
||||
export const LEVEL_1_EBML_IDS: EBMLId[] = [
|
||||
EBMLId.EBMLMaxIDLength,
|
||||
EBMLId.EBMLMaxSizeLength,
|
||||
EBMLId.SeekHead,
|
||||
EBMLId.Info,
|
||||
EBMLId.Cluster,
|
||||
@@ -585,6 +584,28 @@ export class EBMLReader {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Searches for the next occurrence of an element ID using a naive byte-wise search. */
|
||||
async resync(ids: EBMLId[], until: number) {
|
||||
const loadChunkSize = 2 ** 20; // 1 MiB
|
||||
const idsSet = new Set(ids);
|
||||
|
||||
while (this.pos <= until - MIN_HEADER_SIZE) {
|
||||
if (!this.reader.rangeIsLoaded(this.pos, Math.min(this.pos + MAX_HEADER_SIZE, until))) {
|
||||
await this.reader.loadRange(this.pos, Math.min(this.pos + loadChunkSize, until));
|
||||
}
|
||||
|
||||
const elementStartPos = this.pos;
|
||||
const elementId = this.readElementId();
|
||||
if (elementId !== null && idsSet.has(elementId)) {
|
||||
return elementStartPos;
|
||||
}
|
||||
|
||||
this.pos = elementStartPos + 1;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export const CODEC_STRING_MAP: Partial<Record<MediaCodec, string>> = {
|
||||
|
||||
@@ -56,6 +56,7 @@ import {
|
||||
EBMLId,
|
||||
EBMLReader,
|
||||
LEVEL_0_AND_1_EBML_IDS,
|
||||
LEVEL_1_EBML_IDS,
|
||||
MAX_HEADER_SIZE,
|
||||
MIN_HEADER_SIZE,
|
||||
readVarInt,
|
||||
@@ -175,6 +176,7 @@ const METADATA_ELEMENTS = [
|
||||
{ id: EBMLId.Tracks, flag: 'tracksSeen' },
|
||||
{ id: EBMLId.Cues, flag: 'cuesSeen' },
|
||||
] as const;
|
||||
const MAX_RESYNC_LENGTH = 10 * 2 ** 20; // 10 MiB
|
||||
|
||||
export class MatroskaDemuxer extends Demuxer {
|
||||
metadataReader: EBMLReader;
|
||||
@@ -330,8 +332,22 @@ export class MatroskaDemuxer extends Demuxer {
|
||||
|
||||
const elementStartPos = this.metadataReader.pos;
|
||||
const header = this.metadataReader.readElementHeader();
|
||||
if (!header) {
|
||||
break;
|
||||
|
||||
if (!header || !LEVEL_1_EBML_IDS.includes(header.id)) {
|
||||
// Potential junk. Let's try to resync
|
||||
|
||||
this.metadataReader.pos = elementStartPos;
|
||||
const nextPos = await this.metadataReader.resync(
|
||||
LEVEL_1_EBML_IDS,
|
||||
Math.min(this.currentSegment.elementEndPos, this.metadataReader.pos + MAX_RESYNC_LENGTH),
|
||||
);
|
||||
|
||||
if (nextPos) {
|
||||
this.metadataReader.pos = nextPos;
|
||||
continue;
|
||||
} else {
|
||||
break; // Resync failed
|
||||
}
|
||||
}
|
||||
|
||||
const { id, size } = header;
|
||||
@@ -352,6 +368,10 @@ export class MatroskaDemuxer extends Demuxer {
|
||||
}
|
||||
}
|
||||
|
||||
if (size !== null) {
|
||||
this.metadataReader.pos = dataStartPos + size;
|
||||
}
|
||||
|
||||
if (this.currentSegment.infoSeen && this.currentSegment.tracksSeen && this.currentSegment.cuesSeen) {
|
||||
// No need to search anymore, we have everything
|
||||
break;
|
||||
@@ -381,10 +401,15 @@ export class MatroskaDemuxer extends Demuxer {
|
||||
if (size === null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.metadataReader.pos = dataStartPos + size;
|
||||
if (!clusterEncountered) {
|
||||
const seekEntry = this.currentSegment.seekEntries.find(entry => entry.id === EBMLId.Cluster);
|
||||
|
||||
if (!clusterEncountered) {
|
||||
if (seekEntry) {
|
||||
// The seek head points us to the first cluster, nice
|
||||
this.currentSegment.clusterSeekStartPos = segmentDataStart + seekEntry.segmentPosition;
|
||||
} else {
|
||||
this.currentSegment.clusterSeekStartPos = this.metadataReader.pos;
|
||||
}
|
||||
}
|
||||
@@ -1609,8 +1634,24 @@ abstract class MatroskaTrackBacking implements InputTrackBacking {
|
||||
await metadataReader.reader.loadRange(metadataReader.pos, metadataReader.pos + MAX_HEADER_SIZE);
|
||||
const elementStartPos = metadataReader.pos;
|
||||
const elementHeader = metadataReader.readElementHeader();
|
||||
if (!elementHeader) {
|
||||
break;
|
||||
|
||||
if (!elementHeader || !LEVEL_1_EBML_IDS.includes(elementHeader.id)) {
|
||||
// There's an element here that shouldn't be here (or Void). Might be garbage. In this case, let's
|
||||
// try and resync to the next valid element.
|
||||
|
||||
metadataReader.pos = elementStartPos;
|
||||
|
||||
const nextPos = await metadataReader.resync(
|
||||
LEVEL_1_EBML_IDS,
|
||||
Math.min(segment.elementEndPos, metadataReader.pos + MAX_RESYNC_LENGTH),
|
||||
);
|
||||
|
||||
if (nextPos) {
|
||||
metadataReader.pos = nextPos;
|
||||
continue;
|
||||
} else {
|
||||
break; // Resync failed
|
||||
}
|
||||
}
|
||||
|
||||
const id = elementHeader.id;
|
||||
|
||||
Reference in New Issue
Block a user