Compare commits

...
3 Commits
7 changed files with 56 additions and 13 deletions
+2
View File
@@ -49,6 +49,8 @@ Core features include:
npm install mediabunny npm install mediabunny
``` ```
Requires any JavaScript environment that can run ECMAScript 2021 or later. Mediabunny is expected to be run in modern browsers. For types, TypeScript 5.7 or later is required.
### Read file metadata ### Read file metadata
```js ```js
+4
View File
@@ -17,6 +17,10 @@ bun add mediabunny
``` ```
::: :::
::: info
Requires any JavaScript environment that can run ECMAScript 2021 or later. Mediabunny is expected to be run in modern browsers. For types, TypeScript 5.7 or later is required.
:::
Then, simply import it like this: Then, simply import it like this:
```ts ```ts
import { ... } from 'mediabunny'; // ESM import { ... } from 'mediabunny'; // ESM
+12
View File
@@ -286,6 +286,18 @@ See [Media sinks](./media-sinks) for a full list of sinks.
### Examples ### Examples
Loop over all raw encoded packets of a track:
```ts
import { EncodedPacketSink } from 'mediabunny';
const videoTrack = await input.getPrimaryVideoTrack();
const sink = new EncodedPacketSink(videoTrack);
for await (const packet of sink.packets()) {
console.log(packet.timestamp);
}
```
Here we iterate over all samples (frames) of a video track: Here we iterate over all samples (frames) of a video track:
```ts ```ts
import { VideoSampleSink } from 'mediabunny'; import { VideoSampleSink } from 'mediabunny';
+6 -6
View File
@@ -1,16 +1,16 @@
{ {
"name": "mediabunny", "name": "mediabunny",
"version": "1.3.0", "version": "1.3.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "mediabunny", "name": "mediabunny",
"version": "1.3.0", "version": "1.3.2",
"license": "MPL-2.0", "license": "MPL-2.0",
"dependencies": { "dependencies": {
"@types/dom-mediacapture-transform": "^0.1.11", "@types/dom-mediacapture-transform": "^0.1.11",
"@types/dom-webcodecs": "^0.1.15" "@types/dom-webcodecs": "0.1.13"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.22.0", "@eslint/js": "^9.22.0",
@@ -2443,9 +2443,9 @@
} }
}, },
"node_modules/@types/dom-webcodecs": { "node_modules/@types/dom-webcodecs": {
"version": "0.1.15", "version": "0.1.13",
"resolved": "https://registry.npmjs.org/@types/dom-webcodecs/-/dom-webcodecs-0.1.15.tgz", "resolved": "https://registry.npmjs.org/@types/dom-webcodecs/-/dom-webcodecs-0.1.13.tgz",
"integrity": "sha512-omOlCPvTWyPm4ZE5bZUhlSvnHM2ZWM2U+1cPiYFL/e8aV5O9MouELp+L4dMKNTON0nTeHqEg+KWDfFQMY5Wkaw==", "integrity": "sha512-O5hkiFIcjjszPIYyUSyvScyvrBoV3NOEEZx/pMlsu44TKzWNkLVBBxnxJz42in5n3QIolYOcBYFCPZZ0h8SkwQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/estree": { "node_modules/@types/estree": {
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"name": "mediabunny", "name": "mediabunny",
"author": "Vanilagy", "author": "Vanilagy",
"version": "1.3.0", "version": "1.3.2",
"description": "Pure TypeScript media toolkit for reading, writing, and converting media files, directly in the browser.", "description": "Pure TypeScript media toolkit for reading, writing, and converting media files, directly in the browser.",
"type": "module", "type": "module",
"main": "./dist/bundles/mediabunny.cjs", "main": "./dist/bundles/mediabunny.cjs",
@@ -49,7 +49,7 @@
}, },
"dependencies": { "dependencies": {
"@types/dom-mediacapture-transform": "^0.1.11", "@types/dom-mediacapture-transform": "^0.1.11",
"@types/dom-webcodecs": "^0.1.15" "@types/dom-webcodecs": "0.1.13"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.22.0", "@eslint/js": "^9.22.0",
+3 -3
View File
@@ -515,15 +515,15 @@ export const retriedFetch = async (
try { try {
return await fetch(url, requestInit); return await fetch(url, requestInit);
} catch (error) { } catch (error) {
console.error('Retrying failed fetch. Error:', error);
attempts++; attempts++;
const retryDelayInSeconds = getRetryDelay(attempts); const retryDelayInSeconds = getRetryDelay(attempts);
if (retryDelayInSeconds === null) { if (retryDelayInSeconds === null) {
throw error; throw error;
} }
console.error('Retrying failed fetch. Error:', error);
if (!Number.isFinite(retryDelayInSeconds) || retryDelayInSeconds < 0) { if (!Number.isFinite(retryDelayInSeconds) || retryDelayInSeconds < 0) {
throw new TypeError('Retry delay must be a non-negative finite number.'); throw new TypeError('Retry delay must be a non-negative finite number.');
} }
+27 -2
View File
@@ -218,7 +218,8 @@ export class UrlSource extends Source {
const buffer = await response.arrayBuffer(); const buffer = await response.arrayBuffer();
if (!range) { if (response.status === 200) {
// The server didn't return 206 Partial Content, so it's not a range response
this._fullData = buffer; this._fullData = buffer;
} }
@@ -252,6 +253,26 @@ export class UrlSource extends Source {
return this._fullData.byteLength; return this._fullData.byteLength;
} }
// First, try a HEAD request to get the size
try {
const headResponse = await retriedFetch(
this._url,
mergeObjectsDeeply(this._options.requestInit ?? {}, {
method: 'HEAD',
}),
this._options.getRetryDelay ?? (() => null),
);
if (headResponse.ok) {
const contentLength = headResponse.headers.get('Content-Length');
if (contentLength) {
return parseInt(contentLength);
}
}
} catch {
// We tried
}
// Try a range request to get the Content-Range header // Try a range request to get the Content-Range header
const rangeResponse = await retriedFetch( const rangeResponse = await retriedFetch(
this._url, this._url,
@@ -267,9 +288,13 @@ export class UrlSource extends Source {
if (contentRange) { if (contentRange) {
const match = contentRange.match(/bytes \d+-\d+\/(\d+)/); const match = contentRange.match(/bytes \d+-\d+\/(\d+)/);
if (match && match[1]) { if (match && match[1]) {
return parseInt(match[1], 10); return parseInt(match[1]);
} }
} }
} else if (rangeResponse.status === 200) {
// The server just returned the whole thing
this._fullData = await rangeResponse.arrayBuffer();
return this._fullData.byteLength;
} }
// If the range request didn't provide the size, make a full GET request // If the range request didn't provide the size, make a full GET request