mediabunny 1.46.0 → 1.48.0
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/dist/bundles/mediabunny.cjs +340 -215
- package/dist/bundles/mediabunny.min.cjs +11 -11
- package/dist/bundles/mediabunny.min.mjs +11 -11
- package/dist/bundles/mediabunny.mjs +340 -215
- package/dist/bundles/mediabunny.node.cjs +340 -215
- package/dist/mediabunny.d.ts +82 -4
- package/dist/modules/src/adts/adts-demuxer.d.ts +1 -0
- package/dist/modules/src/adts/adts-demuxer.d.ts.map +1 -1
- package/dist/modules/src/adts/adts-demuxer.js +3 -0
- package/dist/modules/src/conversion.d.ts.map +1 -1
- package/dist/modules/src/conversion.js +91 -163
- package/dist/modules/src/encode.d.ts +4 -0
- package/dist/modules/src/encode.d.ts.map +1 -1
- package/dist/modules/src/encode.js +6 -0
- package/dist/modules/src/flac/flac-demuxer.d.ts +1 -0
- package/dist/modules/src/flac/flac-demuxer.d.ts.map +1 -1
- package/dist/modules/src/flac/flac-demuxer.js +3 -0
- package/dist/modules/src/hls/hls-demuxer.d.ts.map +1 -1
- package/dist/modules/src/hls/hls-demuxer.js +6 -1
- package/dist/modules/src/hls/hls-segmented-input.d.ts +1 -0
- package/dist/modules/src/hls/hls-segmented-input.d.ts.map +1 -1
- package/dist/modules/src/hls/hls-segmented-input.js +28 -11
- package/dist/modules/src/index.d.ts +2 -2
- package/dist/modules/src/index.d.ts.map +1 -1
- package/dist/modules/src/input-format.d.ts +21 -0
- package/dist/modules/src/input-format.d.ts.map +1 -1
- package/dist/modules/src/input-format.js +9 -0
- package/dist/modules/src/input-track.d.ts +17 -0
- package/dist/modules/src/input-track.d.ts.map +1 -1
- package/dist/modules/src/input-track.js +20 -0
- package/dist/modules/src/input.d.ts.map +1 -1
- package/dist/modules/src/input.js +6 -2
- package/dist/modules/src/isobmff/isobmff-demuxer.d.ts.map +1 -1
- package/dist/modules/src/isobmff/isobmff-demuxer.js +11 -7
- package/dist/modules/src/matroska/matroska-demuxer.js +3 -0
- package/dist/modules/src/media-sink.d.ts +20 -1
- package/dist/modules/src/media-sink.d.ts.map +1 -1
- package/dist/modules/src/media-sink.js +26 -3
- package/dist/modules/src/media-source.d.ts.map +1 -1
- package/dist/modules/src/media-source.js +14 -2
- package/dist/modules/src/mp3/mp3-demuxer.d.ts +1 -0
- package/dist/modules/src/mp3/mp3-demuxer.d.ts.map +1 -1
- package/dist/modules/src/mp3/mp3-demuxer.js +3 -0
- package/dist/modules/src/mpeg-ts/mpeg-ts-demuxer.js +3 -0
- package/dist/modules/src/ogg/ogg-demuxer.d.ts +1 -0
- package/dist/modules/src/ogg/ogg-demuxer.d.ts.map +1 -1
- package/dist/modules/src/ogg/ogg-demuxer.js +3 -0
- package/dist/modules/src/resample.d.ts +1 -4
- package/dist/modules/src/resample.d.ts.map +1 -1
- package/dist/modules/src/resample.js +9 -9
- package/dist/modules/src/sample.d.ts +6 -0
- package/dist/modules/src/sample.d.ts.map +1 -1
- package/dist/modules/src/sample.js +62 -0
- package/dist/modules/src/segmented-input.d.ts +7 -1
- package/dist/modules/src/segmented-input.d.ts.map +1 -1
- package/dist/modules/src/segmented-input.js +13 -1
- package/dist/modules/src/source.d.ts +13 -3
- package/dist/modules/src/source.d.ts.map +1 -1
- package/dist/modules/src/source.js +19 -3
- package/dist/modules/src/tsconfig.tsbuildinfo +1 -1
- package/dist/modules/src/wave/wave-demuxer.d.ts +1 -0
- package/dist/modules/src/wave/wave-demuxer.d.ts.map +1 -1
- package/dist/modules/src/wave/wave-demuxer.js +3 -0
- package/package.json +1 -1
- package/src/adts/adts-demuxer.ts +4 -0
- package/src/conversion.ts +108 -215
- package/src/encode.ts +10 -0
- package/src/flac/flac-demuxer.ts +4 -0
- package/src/hls/hls-demuxer.ts +8 -1
- package/src/hls/hls-segmented-input.ts +32 -12
- package/src/index.ts +2 -0
- package/src/input-format.ts +33 -0
- package/src/input-track.ts +23 -0
- package/src/input.ts +6 -2
- package/src/isobmff/isobmff-demuxer.ts +13 -7
- package/src/matroska/matroska-demuxer.ts +4 -0
- package/src/media-sink.ts +54 -3
- package/src/media-source.ts +20 -2
- package/src/mp3/mp3-demuxer.ts +4 -0
- package/src/mpeg-ts/mpeg-ts-demuxer.ts +4 -0
- package/src/ogg/ogg-demuxer.ts +4 -0
- package/src/resample.ts +10 -15
- package/src/sample.ts +68 -0
- package/src/segmented-input.ts +23 -2
- package/src/source.ts +26 -5
- package/src/wave/wave-demuxer.ts +4 -0
|
@@ -6890,35 +6890,39 @@ var Mediabunny = (() => {
|
|
|
6890
6890
|
this.readContiguousBoxes(slice.slice(contentStartPos, boxInfo.contentSize));
|
|
6891
6891
|
if (this.currentTrack) {
|
|
6892
6892
|
const trackData = this.currentFragment.trackData.get(this.currentTrack.id);
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
const currentSample = trackData.samples[currentEntry.sampleIndex];
|
|
6899
|
-
if (trackData.firstKeyFrameTimestamp === null && currentSample.isKeyFrame) {
|
|
6900
|
-
trackData.firstKeyFrameTimestamp = currentSample.presentationTimestamp;
|
|
6893
|
+
cond:
|
|
6894
|
+
if (trackData) {
|
|
6895
|
+
if (trackData.samples.length === 0) {
|
|
6896
|
+
this.currentFragment.trackData.delete(this.currentTrack.id);
|
|
6897
|
+
break cond;
|
|
6901
6898
|
}
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
const
|
|
6905
|
-
currentSample
|
|
6899
|
+
trackData.presentationTimestamps = trackData.samples.map((x, i) => ({ presentationTimestamp: x.presentationTimestamp, sampleIndex: i })).sort((a, b) => a.presentationTimestamp - b.presentationTimestamp);
|
|
6900
|
+
for (let i = 0; i < trackData.presentationTimestamps.length; i++) {
|
|
6901
|
+
const currentEntry = trackData.presentationTimestamps[i];
|
|
6902
|
+
const currentSample = trackData.samples[currentEntry.sampleIndex];
|
|
6903
|
+
if (trackData.firstKeyFrameTimestamp === null && currentSample.isKeyFrame) {
|
|
6904
|
+
trackData.firstKeyFrameTimestamp = currentSample.presentationTimestamp;
|
|
6905
|
+
}
|
|
6906
|
+
if (i < trackData.presentationTimestamps.length - 1) {
|
|
6907
|
+
const nextEntry = trackData.presentationTimestamps[i + 1];
|
|
6908
|
+
const duration = nextEntry.presentationTimestamp - currentEntry.presentationTimestamp;
|
|
6909
|
+
currentSample.duration = duration;
|
|
6910
|
+
}
|
|
6911
|
+
}
|
|
6912
|
+
const firstSample = trackData.samples[trackData.presentationTimestamps[0].sampleIndex];
|
|
6913
|
+
const lastSample = trackData.samples[last(trackData.presentationTimestamps).sampleIndex];
|
|
6914
|
+
trackData.startTimestamp = firstSample.presentationTimestamp;
|
|
6915
|
+
trackData.endTimestamp = lastSample.presentationTimestamp + lastSample.duration;
|
|
6916
|
+
const { currentFragmentState } = this.currentTrack;
|
|
6917
|
+
assert(currentFragmentState);
|
|
6918
|
+
if (currentFragmentState.startTimestamp !== null) {
|
|
6919
|
+
offsetFragmentTrackDataByTimestamp(trackData, currentFragmentState.startTimestamp);
|
|
6920
|
+
trackData.startTimestampIsFinal = true;
|
|
6921
|
+
}
|
|
6922
|
+
if (currentFragmentState.encryptionAuxInfo && !trackData.samples[0].encryption) {
|
|
6923
|
+
trackData.encryptionAuxInfo = currentFragmentState.encryptionAuxInfo;
|
|
6906
6924
|
}
|
|
6907
6925
|
}
|
|
6908
|
-
const firstSample = trackData.samples[trackData.presentationTimestamps[0].sampleIndex];
|
|
6909
|
-
const lastSample = trackData.samples[last(trackData.presentationTimestamps).sampleIndex];
|
|
6910
|
-
trackData.startTimestamp = firstSample.presentationTimestamp;
|
|
6911
|
-
trackData.endTimestamp = lastSample.presentationTimestamp + lastSample.duration;
|
|
6912
|
-
const { currentFragmentState } = this.currentTrack;
|
|
6913
|
-
assert(currentFragmentState);
|
|
6914
|
-
if (currentFragmentState.startTimestamp !== null) {
|
|
6915
|
-
offsetFragmentTrackDataByTimestamp(trackData, currentFragmentState.startTimestamp);
|
|
6916
|
-
trackData.startTimestampIsFinal = true;
|
|
6917
|
-
}
|
|
6918
|
-
if (currentFragmentState.encryptionAuxInfo && !trackData.samples[0].encryption) {
|
|
6919
|
-
trackData.encryptionAuxInfo = currentFragmentState.encryptionAuxInfo;
|
|
6920
|
-
}
|
|
6921
|
-
}
|
|
6922
6926
|
this.currentTrack.currentFragmentState = null;
|
|
6923
6927
|
this.currentTrack = null;
|
|
6924
6928
|
}
|
|
@@ -7051,10 +7055,6 @@ var Mediabunny = (() => {
|
|
|
7051
7055
|
};
|
|
7052
7056
|
this.currentFragment.trackData.set(track.id, trackData);
|
|
7053
7057
|
}
|
|
7054
|
-
if (sampleCount === 0) {
|
|
7055
|
-
this.currentFragment.implicitBaseDataOffset = trackData.currentOffset;
|
|
7056
|
-
break;
|
|
7057
|
-
}
|
|
7058
7058
|
for (let i = 0; i < sampleCount; i++) {
|
|
7059
7059
|
let sampleDuration;
|
|
7060
7060
|
if (sampleDurationPresent) {
|
|
@@ -7100,6 +7100,7 @@ var Mediabunny = (() => {
|
|
|
7100
7100
|
trackData.currentOffset += sampleSize;
|
|
7101
7101
|
trackData.currentTimestamp += sampleDuration;
|
|
7102
7102
|
}
|
|
7103
|
+
this.currentFragment.implicitBaseDataOffset = trackData.currentOffset;
|
|
7103
7104
|
}
|
|
7104
7105
|
;
|
|
7105
7106
|
break;
|
|
@@ -7577,6 +7578,9 @@ var Mediabunny = (() => {
|
|
|
7577
7578
|
isRelativeToUnixEpoch() {
|
|
7578
7579
|
return false;
|
|
7579
7580
|
}
|
|
7581
|
+
getUnixTimeForTimestamp() {
|
|
7582
|
+
return null;
|
|
7583
|
+
}
|
|
7580
7584
|
getDisposition() {
|
|
7581
7585
|
return this.internalTrack.disposition;
|
|
7582
7586
|
}
|
|
@@ -10454,6 +10458,9 @@ var Mediabunny = (() => {
|
|
|
10454
10458
|
isRelativeToUnixEpoch() {
|
|
10455
10459
|
return false;
|
|
10456
10460
|
}
|
|
10461
|
+
getUnixTimeForTimestamp() {
|
|
10462
|
+
return null;
|
|
10463
|
+
}
|
|
10457
10464
|
getDisposition() {
|
|
10458
10465
|
return this.internalTrack.disposition;
|
|
10459
10466
|
}
|
|
@@ -11074,6 +11081,9 @@ var Mediabunny = (() => {
|
|
|
11074
11081
|
isRelativeToUnixEpoch() {
|
|
11075
11082
|
return false;
|
|
11076
11083
|
}
|
|
11084
|
+
getUnixTimeForTimestamp() {
|
|
11085
|
+
return null;
|
|
11086
|
+
}
|
|
11077
11087
|
getPairingMask() {
|
|
11078
11088
|
return 1n;
|
|
11079
11089
|
}
|
|
@@ -11642,6 +11652,9 @@ var Mediabunny = (() => {
|
|
|
11642
11652
|
isRelativeToUnixEpoch() {
|
|
11643
11653
|
return false;
|
|
11644
11654
|
}
|
|
11655
|
+
getUnixTimeForTimestamp() {
|
|
11656
|
+
return null;
|
|
11657
|
+
}
|
|
11645
11658
|
getPairingMask() {
|
|
11646
11659
|
return 1n;
|
|
11647
11660
|
}
|
|
@@ -12414,6 +12427,9 @@ var Mediabunny = (() => {
|
|
|
12414
12427
|
isRelativeToUnixEpoch() {
|
|
12415
12428
|
return false;
|
|
12416
12429
|
}
|
|
12430
|
+
getUnixTimeForTimestamp() {
|
|
12431
|
+
return null;
|
|
12432
|
+
}
|
|
12417
12433
|
getPairingMask() {
|
|
12418
12434
|
return 1n;
|
|
12419
12435
|
}
|
|
@@ -12712,6 +12728,9 @@ var Mediabunny = (() => {
|
|
|
12712
12728
|
isRelativeToUnixEpoch() {
|
|
12713
12729
|
return false;
|
|
12714
12730
|
}
|
|
12731
|
+
getUnixTimeForTimestamp() {
|
|
12732
|
+
return null;
|
|
12733
|
+
}
|
|
12715
12734
|
getPairingMask() {
|
|
12716
12735
|
return 1n;
|
|
12717
12736
|
}
|
|
@@ -13332,6 +13351,9 @@ var Mediabunny = (() => {
|
|
|
13332
13351
|
isRelativeToUnixEpoch() {
|
|
13333
13352
|
return false;
|
|
13334
13353
|
}
|
|
13354
|
+
getUnixTimeForTimestamp() {
|
|
13355
|
+
return null;
|
|
13356
|
+
}
|
|
13335
13357
|
getPairingMask() {
|
|
13336
13358
|
return 1n;
|
|
13337
13359
|
}
|
|
@@ -14200,6 +14222,9 @@ var Mediabunny = (() => {
|
|
|
14200
14222
|
isRelativeToUnixEpoch() {
|
|
14201
14223
|
return false;
|
|
14202
14224
|
}
|
|
14225
|
+
getUnixTimeForTimestamp() {
|
|
14226
|
+
return null;
|
|
14227
|
+
}
|
|
14203
14228
|
getPairingMask() {
|
|
14204
14229
|
return 1n;
|
|
14205
14230
|
}
|
|
@@ -15238,6 +15263,15 @@ var Mediabunny = (() => {
|
|
|
15238
15263
|
}
|
|
15239
15264
|
return lastSegment.timestamp + lastSegment.duration;
|
|
15240
15265
|
}
|
|
15266
|
+
async getUnixTimeForTimestamp(timestamp) {
|
|
15267
|
+
let segment = await this.getSegmentAt(timestamp, {});
|
|
15268
|
+
segment ??= await this.getFirstSegment({});
|
|
15269
|
+
if (!segment || segment.unixEpochTimestamp === null) {
|
|
15270
|
+
return null;
|
|
15271
|
+
}
|
|
15272
|
+
const elapsed = timestamp - segment.timestamp;
|
|
15273
|
+
return segment.unixEpochTimestamp + elapsed;
|
|
15274
|
+
}
|
|
15241
15275
|
async getTrackBackings() {
|
|
15242
15276
|
return this.trackBackingsPromise ??= (async () => {
|
|
15243
15277
|
const backings = [];
|
|
@@ -15395,7 +15429,10 @@ var Mediabunny = (() => {
|
|
|
15395
15429
|
async isRelativeToUnixEpoch() {
|
|
15396
15430
|
await this.hydrate();
|
|
15397
15431
|
assert(this.segmentedInput.firstSegment);
|
|
15398
|
-
return this.segmentedInput.firstSegment.
|
|
15432
|
+
return this.segmentedInput.firstSegment.unixEpochTimestamp === this.segmentedInput.firstSegment.timestamp;
|
|
15433
|
+
}
|
|
15434
|
+
getUnixTimeForTimestamp(timestamp) {
|
|
15435
|
+
return this.segmentedInput.getUnixTimeForTimestamp(timestamp);
|
|
15399
15436
|
}
|
|
15400
15437
|
getBitrate() {
|
|
15401
15438
|
return this.delegate(() => this.firstInputTrack._backing.getBitrate());
|
|
@@ -15985,7 +16022,10 @@ var Mediabunny = (() => {
|
|
|
15985
16022
|
throw new TypeError("options.fetchFn, when provided, must be a function.");
|
|
15986
16023
|
}
|
|
15987
16024
|
const urlString = url2 instanceof Request ? url2.url : url2 instanceof URL ? url2.href : url2;
|
|
15988
|
-
super(
|
|
16025
|
+
super(
|
|
16026
|
+
urlString,
|
|
16027
|
+
(request) => new _UrlSource(request.path, this._options)
|
|
16028
|
+
);
|
|
15989
16029
|
/** @internal */
|
|
15990
16030
|
this._offset = 0;
|
|
15991
16031
|
/** @internal */
|
|
@@ -16090,6 +16130,9 @@ var Mediabunny = (() => {
|
|
|
16090
16130
|
if (!response.ok) {
|
|
16091
16131
|
throw new Error(`Error fetching ${String(this._url)}: ${response.status} ${response.statusText}`);
|
|
16092
16132
|
}
|
|
16133
|
+
if (response.redirected) {
|
|
16134
|
+
this.rootPath = response.url;
|
|
16135
|
+
}
|
|
16093
16136
|
outer:
|
|
16094
16137
|
if (this._orchestrator.fileSize === null) {
|
|
16095
16138
|
const contentRange = response.headers.get("Content-Range");
|
|
@@ -16750,6 +16793,12 @@ var Mediabunny = (() => {
|
|
|
16750
16793
|
offset: innerStart
|
|
16751
16794
|
});
|
|
16752
16795
|
} else {
|
|
16796
|
+
promise.catch((error) => {
|
|
16797
|
+
if (this.disposed) {
|
|
16798
|
+
return;
|
|
16799
|
+
}
|
|
16800
|
+
throw error;
|
|
16801
|
+
});
|
|
16753
16802
|
}
|
|
16754
16803
|
return result;
|
|
16755
16804
|
}
|
|
@@ -16834,11 +16883,11 @@ var Mediabunny = (() => {
|
|
|
16834
16883
|
if (worker.pendingSlices.length > 0) {
|
|
16835
16884
|
worker.pendingSlices.forEach((x) => x.reject(error));
|
|
16836
16885
|
worker.pendingSlices.length = 0;
|
|
16837
|
-
} else {
|
|
16886
|
+
} else if (!worker.aborted && !this.disposed) {
|
|
16838
16887
|
throw error;
|
|
16839
16888
|
}
|
|
16840
16889
|
}).finally(() => {
|
|
16841
|
-
if (worker.running) {
|
|
16890
|
+
if (worker.running || this.workers.length >= this.options.maxWorkerCount) {
|
|
16842
16891
|
return;
|
|
16843
16892
|
}
|
|
16844
16893
|
if (this.queuedReads.length > 0) {
|
|
@@ -17151,6 +17200,7 @@ var Mediabunny = (() => {
|
|
|
17151
17200
|
this.streamHasEnded = false;
|
|
17152
17201
|
this.lastSegmentUpdateTime = -Infinity;
|
|
17153
17202
|
this.refreshInterval = 5;
|
|
17203
|
+
this.rootPath = path;
|
|
17154
17204
|
this.demuxer = demuxer;
|
|
17155
17205
|
this.nextLines = lines;
|
|
17156
17206
|
}
|
|
@@ -17186,19 +17236,24 @@ var Mediabunny = (() => {
|
|
|
17186
17236
|
if (!lines) {
|
|
17187
17237
|
var _stack = [];
|
|
17188
17238
|
try {
|
|
17189
|
-
const ref = __using(_stack, await this.demuxer.input._getSourceUncached({ path: this.
|
|
17239
|
+
const ref = __using(_stack, await this.demuxer.input._getSourceUncached({ path: this.rootPath, isRoot: false }));
|
|
17190
17240
|
const reader = new Reader12(ref.source);
|
|
17191
17241
|
const slice = await reader.requestEntireFile();
|
|
17192
17242
|
assert(slice);
|
|
17193
17243
|
lines = readAllLines(slice, slice.length, { ignore: canIgnoreLine });
|
|
17244
|
+
if (ref.source instanceof PathedSource) {
|
|
17245
|
+
this.rootPath = ref.source.rootPath;
|
|
17246
|
+
}
|
|
17194
17247
|
} catch (_) {
|
|
17195
17248
|
var _error = _, _hasError = true;
|
|
17196
17249
|
} finally {
|
|
17197
17250
|
__callDispose(_stack, _error, _hasError);
|
|
17198
17251
|
}
|
|
17199
17252
|
}
|
|
17253
|
+
const offsetTimestampsByDateTime = this.input._formatOptions.hls?.offsetTimestampsByDateTime !== false;
|
|
17200
17254
|
let headerRead = false;
|
|
17201
17255
|
let accumulatedTime = 0;
|
|
17256
|
+
let accumulatedUnixTime = null;
|
|
17202
17257
|
let nextSegmentDuration = null;
|
|
17203
17258
|
let currentKey = null;
|
|
17204
17259
|
let nextSequenceNumber = 0;
|
|
@@ -17234,6 +17289,7 @@ var Mediabunny = (() => {
|
|
|
17234
17289
|
currentFirstSegment = prevLastSegment.firstSegment;
|
|
17235
17290
|
currentInitSegment = prevLastSegment.initSegment;
|
|
17236
17291
|
lastProgramDateTimeSeconds = prevLastSegment.lastProgramDateTimeSeconds;
|
|
17292
|
+
accumulatedUnixTime = prevLastSegment.unixEpochTimestamp !== null ? prevLastSegment.unixEpochTimestamp + prevLastSegment.duration : null;
|
|
17237
17293
|
prevLastSegment = null;
|
|
17238
17294
|
}
|
|
17239
17295
|
}
|
|
@@ -17260,7 +17316,7 @@ var Mediabunny = (() => {
|
|
|
17260
17316
|
view2.setUint32(12, nextSequenceNumber);
|
|
17261
17317
|
key = { ...key, iv };
|
|
17262
17318
|
}
|
|
17263
|
-
const fullPath = joinPaths(this.
|
|
17319
|
+
const fullPath = joinPaths(this.rootPath, line);
|
|
17264
17320
|
const location = {
|
|
17265
17321
|
path: fullPath,
|
|
17266
17322
|
offset: nextByteRange?.offset ?? 0,
|
|
@@ -17268,7 +17324,7 @@ var Mediabunny = (() => {
|
|
|
17268
17324
|
};
|
|
17269
17325
|
const segment = {
|
|
17270
17326
|
timestamp: accumulatedTime,
|
|
17271
|
-
|
|
17327
|
+
unixEpochTimestamp: accumulatedUnixTime,
|
|
17272
17328
|
firstSegment: currentFirstSegment,
|
|
17273
17329
|
sequenceNumber: nextSequenceNumber,
|
|
17274
17330
|
location,
|
|
@@ -17279,6 +17335,9 @@ var Mediabunny = (() => {
|
|
|
17279
17335
|
};
|
|
17280
17336
|
currentFirstSegment ??= segment;
|
|
17281
17337
|
accumulatedTime += nextSegmentDuration;
|
|
17338
|
+
if (accumulatedUnixTime !== null) {
|
|
17339
|
+
accumulatedUnixTime += nextSegmentDuration;
|
|
17340
|
+
}
|
|
17282
17341
|
this.segments.push(segment);
|
|
17283
17342
|
} else {
|
|
17284
17343
|
}
|
|
@@ -17324,7 +17383,7 @@ var Mediabunny = (() => {
|
|
|
17324
17383
|
throw new Error("Invalid #EXT-X-MAP tag; BYTERANGE attribute must have a specified offset.");
|
|
17325
17384
|
}
|
|
17326
17385
|
if (!prevLastSegment) {
|
|
17327
|
-
const fullPath = joinPaths(this.
|
|
17386
|
+
const fullPath = joinPaths(this.rootPath, uri);
|
|
17328
17387
|
const location = {
|
|
17329
17388
|
path: fullPath,
|
|
17330
17389
|
offset: parsedByteRange?.offset ?? 0,
|
|
@@ -17335,7 +17394,7 @@ var Mediabunny = (() => {
|
|
|
17335
17394
|
}
|
|
17336
17395
|
const segment = {
|
|
17337
17396
|
timestamp: accumulatedTime,
|
|
17338
|
-
|
|
17397
|
+
unixEpochTimestamp: accumulatedUnixTime,
|
|
17339
17398
|
firstSegment: null,
|
|
17340
17399
|
sequenceNumber: null,
|
|
17341
17400
|
location,
|
|
@@ -17385,7 +17444,7 @@ var Mediabunny = (() => {
|
|
|
17385
17444
|
}
|
|
17386
17445
|
currentKey = {
|
|
17387
17446
|
method: "AES-128",
|
|
17388
|
-
keyUri: joinPaths(this.
|
|
17447
|
+
keyUri: joinPaths(this.rootPath, uri),
|
|
17389
17448
|
iv,
|
|
17390
17449
|
keyFormat
|
|
17391
17450
|
};
|
|
@@ -17455,13 +17514,17 @@ var Mediabunny = (() => {
|
|
|
17455
17514
|
const lastSegmentEnd = lastSegment.timestamp + lastSegment.duration;
|
|
17456
17515
|
const offset = dateTimeSeconds - lastSegmentEnd;
|
|
17457
17516
|
for (const segment of this.segments) {
|
|
17458
|
-
segment.timestamp
|
|
17459
|
-
|
|
17517
|
+
segment.unixEpochTimestamp = segment.timestamp + offset;
|
|
17518
|
+
if (offsetTimestampsByDateTime) {
|
|
17519
|
+
segment.timestamp = segment.unixEpochTimestamp;
|
|
17520
|
+
}
|
|
17460
17521
|
}
|
|
17461
|
-
accumulatedTime += offset;
|
|
17462
17522
|
}
|
|
17463
17523
|
lastProgramDateTimeSeconds = dateTimeSeconds;
|
|
17464
|
-
|
|
17524
|
+
accumulatedUnixTime = dateTimeSeconds;
|
|
17525
|
+
if (offsetTimestampsByDateTime) {
|
|
17526
|
+
accumulatedTime = dateTimeSeconds;
|
|
17527
|
+
}
|
|
17465
17528
|
} else if (line === TAG_DISCONTINUITY) {
|
|
17466
17529
|
currentFirstSegment = null;
|
|
17467
17530
|
} else if (line.startsWith(TAG_TARGETDURATION)) {
|
|
@@ -17678,10 +17741,10 @@ var Mediabunny = (() => {
|
|
|
17678
17741
|
readMetadata() {
|
|
17679
17742
|
return this.metadataPromise ??= (async () => {
|
|
17680
17743
|
assert(this.input._rootSource instanceof PathedSource);
|
|
17681
|
-
const { rootPath } = this.input._rootSource;
|
|
17682
17744
|
const slice = await this.input._reader.requestEntireFile();
|
|
17683
17745
|
assert(slice);
|
|
17684
17746
|
const lines = readAllLines(slice, slice.length, { ignore: canIgnoreLine });
|
|
17747
|
+
const { rootPath } = this.input._rootSource;
|
|
17685
17748
|
const variantStreams = [];
|
|
17686
17749
|
const mediaTags = [];
|
|
17687
17750
|
for (let i = 1; i < lines.length; i++) {
|
|
@@ -18196,6 +18259,9 @@ var Mediabunny = (() => {
|
|
|
18196
18259
|
isRelativeToUnixEpoch() {
|
|
18197
18260
|
return this.delegate(() => this.internalTrack.backingTrack.isRelativeToUnixEpoch());
|
|
18198
18261
|
}
|
|
18262
|
+
getUnixTimeForTimestamp(timestamp) {
|
|
18263
|
+
return this.delegate(() => this.internalTrack.backingTrack.getUnixTimeForTimestamp(timestamp));
|
|
18264
|
+
}
|
|
18199
18265
|
getBitrate() {
|
|
18200
18266
|
return this.internalTrack.peakBitrate;
|
|
18201
18267
|
}
|
|
@@ -18784,6 +18850,14 @@ var Mediabunny = (() => {
|
|
|
18784
18850
|
throw new TypeError(`${prefix}.isobmff.resolveKeyId, when provided, must be a function.`);
|
|
18785
18851
|
}
|
|
18786
18852
|
}
|
|
18853
|
+
if (options.hls !== void 0) {
|
|
18854
|
+
if (!options.hls || typeof options.hls !== "object") {
|
|
18855
|
+
throw new TypeError(`${prefix}.hls, when provided, must be an object.`);
|
|
18856
|
+
}
|
|
18857
|
+
if (options.hls.offsetTimestampsByDateTime !== void 0 && typeof options.hls.offsetTimestampsByDateTime !== "boolean") {
|
|
18858
|
+
throw new TypeError(`${prefix}.hls.offsetTimestampsByDateTime, when provided, must be a boolean.`);
|
|
18859
|
+
}
|
|
18860
|
+
}
|
|
18787
18861
|
};
|
|
18788
18862
|
|
|
18789
18863
|
// src/decode.ts
|
|
@@ -20577,6 +20651,65 @@ var Mediabunny = (() => {
|
|
|
20577
20651
|
});
|
|
20578
20652
|
}
|
|
20579
20653
|
}
|
|
20654
|
+
/**
|
|
20655
|
+
* Returns a new {@link AudioSample} containing only the frames in the range [startSample, endSample). Both bounds
|
|
20656
|
+
* must lie within this sample's range of frames. The returned sample's timestamp is shifted to match the start of
|
|
20657
|
+
* the trimmed section.
|
|
20658
|
+
*/
|
|
20659
|
+
trim(startSample, endSample = this.numberOfFrames) {
|
|
20660
|
+
if (!Number.isInteger(startSample) || startSample < 0) {
|
|
20661
|
+
throw new TypeError("startSample must be a non-negative integer.");
|
|
20662
|
+
}
|
|
20663
|
+
if (!Number.isInteger(endSample) || endSample < 0) {
|
|
20664
|
+
throw new TypeError("endSample must be a non-negative integer.");
|
|
20665
|
+
}
|
|
20666
|
+
if (startSample > this.numberOfFrames) {
|
|
20667
|
+
throw new RangeError("startSample out of range.");
|
|
20668
|
+
}
|
|
20669
|
+
if (endSample > this.numberOfFrames) {
|
|
20670
|
+
throw new RangeError("endSample out of range.");
|
|
20671
|
+
}
|
|
20672
|
+
if (endSample < startSample) {
|
|
20673
|
+
throw new RangeError("endSample must not be less than startSample.");
|
|
20674
|
+
}
|
|
20675
|
+
if (this._closed) {
|
|
20676
|
+
throw new Error("AudioSample is closed.");
|
|
20677
|
+
}
|
|
20678
|
+
const frameCount = endSample - startSample;
|
|
20679
|
+
const bytesPerSample = getBytesPerSample(this.format);
|
|
20680
|
+
let data;
|
|
20681
|
+
if (formatIsPlanar(this.format)) {
|
|
20682
|
+
const planeSize = frameCount * bytesPerSample;
|
|
20683
|
+
data = new Uint8Array(planeSize * this.numberOfChannels);
|
|
20684
|
+
if (frameCount > 0) {
|
|
20685
|
+
for (let i = 0; i < this.numberOfChannels; i++) {
|
|
20686
|
+
this.copyTo(data.subarray(i * planeSize, (i + 1) * planeSize), {
|
|
20687
|
+
planeIndex: i,
|
|
20688
|
+
format: this.format,
|
|
20689
|
+
frameOffset: startSample,
|
|
20690
|
+
frameCount
|
|
20691
|
+
});
|
|
20692
|
+
}
|
|
20693
|
+
}
|
|
20694
|
+
} else {
|
|
20695
|
+
data = new Uint8Array(frameCount * this.numberOfChannels * bytesPerSample);
|
|
20696
|
+
if (frameCount > 0) {
|
|
20697
|
+
this.copyTo(data, {
|
|
20698
|
+
planeIndex: 0,
|
|
20699
|
+
format: this.format,
|
|
20700
|
+
frameOffset: startSample,
|
|
20701
|
+
frameCount
|
|
20702
|
+
});
|
|
20703
|
+
}
|
|
20704
|
+
}
|
|
20705
|
+
return new _AudioSample({
|
|
20706
|
+
data,
|
|
20707
|
+
format: this.format,
|
|
20708
|
+
sampleRate: this.sampleRate,
|
|
20709
|
+
numberOfChannels: this.numberOfChannels,
|
|
20710
|
+
timestamp: this.timestamp + startSample / this.sampleRate
|
|
20711
|
+
});
|
|
20712
|
+
}
|
|
20580
20713
|
/**
|
|
20581
20714
|
* Closes this audio sample, releasing held resources. Audio samples should be closed as soon as they are not
|
|
20582
20715
|
* needed anymore.
|
|
@@ -20995,6 +21128,9 @@ var Mediabunny = (() => {
|
|
|
20995
21128
|
if (config.onEncoderConfig !== void 0 && typeof config.onEncoderConfig !== "function") {
|
|
20996
21129
|
throw new TypeError("config.onEncoderConfig, when provided, must be a function.");
|
|
20997
21130
|
}
|
|
21131
|
+
if (config.onEncodedSample !== void 0 && typeof config.onEncodedSample !== "function") {
|
|
21132
|
+
throw new TypeError("config.onEncodedSample, when provided, must be a function.");
|
|
21133
|
+
}
|
|
20998
21134
|
validateVideoEncodingAdditionalOptions(config.codec, config);
|
|
20999
21135
|
};
|
|
21000
21136
|
var validateVideoEncodingAdditionalOptions = (codec, options) => {
|
|
@@ -21090,6 +21226,9 @@ var Mediabunny = (() => {
|
|
|
21090
21226
|
if (config.onEncoderConfig !== void 0 && typeof config.onEncoderConfig !== "function") {
|
|
21091
21227
|
throw new TypeError("config.onEncoderConfig, when provided, must be a function.");
|
|
21092
21228
|
}
|
|
21229
|
+
if (config.onEncodedSample !== void 0 && typeof config.onEncodedSample !== "function") {
|
|
21230
|
+
throw new TypeError("config.onEncodedSample, when provided, must be a function.");
|
|
21231
|
+
}
|
|
21093
21232
|
validateAudioEncodingAdditionalOptions(config.codec, config);
|
|
21094
21233
|
};
|
|
21095
21234
|
var validateAudioEncodingAdditionalOptions = (codec, options) => {
|
|
@@ -22779,14 +22918,29 @@ var Mediabunny = (() => {
|
|
|
22779
22918
|
}
|
|
22780
22919
|
};
|
|
22781
22920
|
};
|
|
22921
|
+
var validateVideoSinkDecoderOptions = (decoderOptions) => {
|
|
22922
|
+
if (!decoderOptions || typeof decoderOptions !== "object") {
|
|
22923
|
+
throw new TypeError("decoderOptions must be an object.");
|
|
22924
|
+
}
|
|
22925
|
+
if (decoderOptions.hardwareAcceleration !== void 0 && !["no-preference", "prefer-hardware", "prefer-software"].includes(decoderOptions.hardwareAcceleration)) {
|
|
22926
|
+
throw new TypeError(
|
|
22927
|
+
"decoderOptions.hardwareAcceleration, when provided, must be 'no-preference', 'prefer-hardware' or 'prefer-software'."
|
|
22928
|
+
);
|
|
22929
|
+
}
|
|
22930
|
+
if (decoderOptions.optimizeForLatency !== void 0 && typeof decoderOptions.optimizeForLatency !== "boolean") {
|
|
22931
|
+
throw new TypeError("decoderOptions.optimizeForLatency, when provided, must be a boolean.");
|
|
22932
|
+
}
|
|
22933
|
+
};
|
|
22782
22934
|
var VideoSampleSink = class extends BaseMediaSampleSink {
|
|
22783
22935
|
/** Creates a new {@link VideoSampleSink} for the given {@link InputVideoTrack}. */
|
|
22784
|
-
constructor(videoTrack) {
|
|
22936
|
+
constructor(videoTrack, decoderOptions = {}) {
|
|
22785
22937
|
if (!(videoTrack instanceof InputVideoTrack)) {
|
|
22786
22938
|
throw new TypeError("videoTrack must be an InputVideoTrack.");
|
|
22787
22939
|
}
|
|
22940
|
+
validateVideoSinkDecoderOptions(decoderOptions);
|
|
22788
22941
|
super();
|
|
22789
22942
|
this._track = videoTrack;
|
|
22943
|
+
this._decoderOptions = decoderOptions;
|
|
22790
22944
|
}
|
|
22791
22945
|
/** @internal */
|
|
22792
22946
|
async _createDecoder(onSample, onError) {
|
|
@@ -22797,9 +22951,14 @@ var Mediabunny = (() => {
|
|
|
22797
22951
|
}
|
|
22798
22952
|
const codec = await this._track.getCodec();
|
|
22799
22953
|
const rotation = await this._track.getRotation();
|
|
22800
|
-
|
|
22954
|
+
let decoderConfig = await this._track.getDecoderConfig();
|
|
22801
22955
|
const timeResolution = await this._track.getTimeResolution();
|
|
22802
22956
|
assert(codec && decoderConfig);
|
|
22957
|
+
decoderConfig = {
|
|
22958
|
+
...decoderConfig,
|
|
22959
|
+
hardwareAcceleration: this._decoderOptions.hardwareAcceleration,
|
|
22960
|
+
optimizeForLatency: this._decoderOptions.optimizeForLatency
|
|
22961
|
+
};
|
|
22803
22962
|
return new VideoDecoderWrapper(onSample, onError, codec, decoderConfig, rotation, timeResolution);
|
|
22804
22963
|
}
|
|
22805
22964
|
/** @internal */
|
|
@@ -22889,11 +23048,14 @@ var Mediabunny = (() => {
|
|
|
22889
23048
|
if (options.poolSize !== void 0 && (typeof options.poolSize !== "number" || !Number.isInteger(options.poolSize) || options.poolSize < 0)) {
|
|
22890
23049
|
throw new TypeError("poolSize must be a non-negative integer.");
|
|
22891
23050
|
}
|
|
23051
|
+
if (options.decoderOptions !== void 0) {
|
|
23052
|
+
validateVideoSinkDecoderOptions(options.decoderOptions);
|
|
23053
|
+
}
|
|
22892
23054
|
this._videoTrack = videoTrack;
|
|
22893
23055
|
this._alpha = options.alpha ?? false;
|
|
22894
23056
|
this._options = options;
|
|
22895
23057
|
this._fit = options.fit ?? "fill";
|
|
22896
|
-
this._videoSampleSink = new VideoSampleSink(videoTrack);
|
|
23058
|
+
this._videoSampleSink = new VideoSampleSink(videoTrack, options.decoderOptions);
|
|
22897
23059
|
this._canvasPool = Array.from({ length: options.poolSize ?? 0 }, () => null);
|
|
22898
23060
|
}
|
|
22899
23061
|
/** @internal */
|
|
@@ -23536,6 +23698,26 @@ var Mediabunny = (() => {
|
|
|
23536
23698
|
async isRelativeToUnixEpoch() {
|
|
23537
23699
|
return this._backing.isRelativeToUnixEpoch();
|
|
23538
23700
|
}
|
|
23701
|
+
/**
|
|
23702
|
+
* Returns the Unix time (in seconds since January 1, 1970 00:00:00 UTC) that the given track timestamp (in seconds)
|
|
23703
|
+
* maps to, or `null` if there is no such mapping. This provides a piecewise-continuous mapping from this track's
|
|
23704
|
+
* timestamp space into wall-clock time. Such mapping exists, for example, for HLS playlists with
|
|
23705
|
+
* `#EXT-X-PROGRAM-DATE-TIME` tags present.
|
|
23706
|
+
*
|
|
23707
|
+
* This mapping can be available even when {@link InputTrack.isRelativeToUnixEpoch} is `false`, for example for HLS
|
|
23708
|
+
* streams with program date time information but with {@link HlsInputFormatOptions.offsetTimestampsByDateTime}
|
|
23709
|
+
* set to `false`.
|
|
23710
|
+
*/
|
|
23711
|
+
async getUnixTimeForTimestamp(timestamp) {
|
|
23712
|
+
return this._backing.getUnixTimeForTimestamp(timestamp);
|
|
23713
|
+
}
|
|
23714
|
+
/**
|
|
23715
|
+
* Whether the track's timestamps can be mapped to Unix wall clock time via
|
|
23716
|
+
* {@link InputTrack.getUnixTimeForTimestamp}.
|
|
23717
|
+
*/
|
|
23718
|
+
async hasUnixTimeMapping() {
|
|
23719
|
+
return await this._backing.getUnixTimeForTimestamp(await this.getFirstTimestamp()) !== null;
|
|
23720
|
+
}
|
|
23539
23721
|
/** Returns the track's disposition, i.e. information about its intended usage. */
|
|
23540
23722
|
async getDisposition() {
|
|
23541
23723
|
return this._backing.getDisposition();
|
|
@@ -24537,7 +24719,10 @@ var Mediabunny = (() => {
|
|
|
24537
24719
|
ref.free();
|
|
24538
24720
|
}
|
|
24539
24721
|
this._sourceRefs.length = 0;
|
|
24540
|
-
|
|
24722
|
+
if (this._demuxerPromise) {
|
|
24723
|
+
void this._demuxerPromise.then((demuxer) => demuxer.dispose()).catch(() => {
|
|
24724
|
+
});
|
|
24725
|
+
}
|
|
24541
24726
|
}
|
|
24542
24727
|
/**
|
|
24543
24728
|
* Calls `.dispose()` on the input, implementing the `Disposable` interface for use with
|
|
@@ -31990,17 +32175,17 @@ ${cue.notes ?? ""}`;
|
|
|
31990
32175
|
constructor(options) {
|
|
31991
32176
|
this.sourceSampleRate = null;
|
|
31992
32177
|
this.sourceNumberOfChannels = null;
|
|
32178
|
+
this.startTime = null;
|
|
32179
|
+
/** Start frame of current buffer */
|
|
32180
|
+
this.bufferStartFrame = 0;
|
|
31993
32181
|
/** The highest index written to in the current buffer */
|
|
31994
32182
|
this.maxWrittenFrame = null;
|
|
31995
32183
|
this.targetSampleRate = options.targetSampleRate;
|
|
31996
32184
|
this.targetNumberOfChannels = options.targetNumberOfChannels;
|
|
31997
|
-
this.endTime = options.endTime;
|
|
31998
32185
|
this.onSample = options.onSample;
|
|
31999
32186
|
this.bufferSizeInFrames = Math.floor(this.targetSampleRate * 5);
|
|
32000
32187
|
this.bufferSizeInSamples = this.bufferSizeInFrames * this.targetNumberOfChannels;
|
|
32001
32188
|
this.outputBuffer = new Float32Array(this.bufferSizeInSamples);
|
|
32002
|
-
this.bufferStartFrame = Math.floor(options.startTime * this.targetSampleRate);
|
|
32003
|
-
this.timestampOffset = options.startTime - this.bufferStartFrame / this.targetSampleRate;
|
|
32004
32189
|
}
|
|
32005
32190
|
/**
|
|
32006
32191
|
* Sets up the channel mixer to handle up/downmixing in the case where input and output channel counts don't match.
|
|
@@ -32090,16 +32275,18 @@ ${cue.notes ?? ""}`;
|
|
|
32090
32275
|
if (this.sourceSampleRate === null) {
|
|
32091
32276
|
this.sourceSampleRate = audioSample.sampleRate;
|
|
32092
32277
|
this.sourceNumberOfChannels = audioSample.numberOfChannels;
|
|
32278
|
+
this.startTime = audioSample.timestamp;
|
|
32093
32279
|
this.tempSourceBuffer = new Float32Array(this.sourceSampleRate * this.sourceNumberOfChannels);
|
|
32094
32280
|
this.doChannelMixerSetup();
|
|
32095
32281
|
}
|
|
32282
|
+
assert(this.startTime !== null);
|
|
32096
32283
|
const requiredSamples = audioSample.numberOfFrames * audioSample.numberOfChannels;
|
|
32097
32284
|
this.ensureTempBufferSize(requiredSamples);
|
|
32098
32285
|
const sourceDataSize = audioSample.allocationSize({ planeIndex: 0, format: "f32" });
|
|
32099
32286
|
const sourceView = new Float32Array(this.tempSourceBuffer.buffer, 0, sourceDataSize / 4);
|
|
32100
32287
|
audioSample.copyTo(sourceView, { planeIndex: 0, format: "f32" });
|
|
32101
|
-
const inputStartTime = audioSample.timestamp;
|
|
32102
|
-
const inputEndTime =
|
|
32288
|
+
const inputStartTime = audioSample.timestamp - this.startTime;
|
|
32289
|
+
const inputEndTime = inputStartTime + audioSample.duration;
|
|
32103
32290
|
const outputStartFrame = Math.floor(inputStartTime * this.targetSampleRate);
|
|
32104
32291
|
const outputEndFrame = Math.ceil(inputEndTime * this.targetSampleRate);
|
|
32105
32292
|
for (let outputFrame = outputStartFrame; outputFrame < outputEndFrame; outputFrame++) {
|
|
@@ -32142,15 +32329,15 @@ ${cue.notes ?? ""}`;
|
|
|
32142
32329
|
if (this.maxWrittenFrame === null) {
|
|
32143
32330
|
return;
|
|
32144
32331
|
}
|
|
32332
|
+
assert(this.startTime !== null);
|
|
32145
32333
|
const samplesWritten = (this.maxWrittenFrame + 1) * this.targetNumberOfChannels;
|
|
32146
32334
|
const outputData = new Float32Array(samplesWritten);
|
|
32147
32335
|
outputData.set(this.outputBuffer.subarray(0, samplesWritten));
|
|
32148
|
-
const timestampSeconds = this.bufferStartFrame / this.targetSampleRate;
|
|
32149
32336
|
const audioSample = new AudioSample({
|
|
32150
32337
|
format: "f32",
|
|
32151
32338
|
sampleRate: this.targetSampleRate,
|
|
32152
32339
|
numberOfChannels: this.targetNumberOfChannels,
|
|
32153
|
-
timestamp:
|
|
32340
|
+
timestamp: this.startTime + this.bufferStartFrame / this.targetSampleRate,
|
|
32154
32341
|
data: outputData
|
|
32155
32342
|
});
|
|
32156
32343
|
await this.onSample(audioSample);
|
|
@@ -32314,6 +32501,7 @@ ${cue.notes ?? ""}`;
|
|
|
32314
32501
|
* So, we keep track of the encoder error and throw it as soon as we get the chance.
|
|
32315
32502
|
*/
|
|
32316
32503
|
this.error = null;
|
|
32504
|
+
this.closed = false;
|
|
32317
32505
|
this.lastMuxerPromise = Promise.resolve();
|
|
32318
32506
|
}
|
|
32319
32507
|
async add(videoSample, shouldClose, encodeOptions) {
|
|
@@ -32450,6 +32638,9 @@ ${cue.notes ?? ""}`;
|
|
|
32450
32638
|
}
|
|
32451
32639
|
}
|
|
32452
32640
|
assert(this.encoderInitialized);
|
|
32641
|
+
if (this.closed) {
|
|
32642
|
+
break;
|
|
32643
|
+
}
|
|
32453
32644
|
const keyFrameInterval = this.encodingConfig.keyFrameInterval ?? 2;
|
|
32454
32645
|
const multipleOfKeyFrameInterval = Math.floor(sampleToEncode.timestamp / keyFrameInterval);
|
|
32455
32646
|
const mergedEncodeOptions = { ...sampleToEncode.encodeOptions, ...encodeOptions };
|
|
@@ -32458,6 +32649,7 @@ ${cue.notes ?? ""}`;
|
|
|
32458
32649
|
keyFrame: mergedEncodeOptions.keyFrame !== void 0 ? mergedEncodeOptions.keyFrame : keyFrameInterval === 0 || multipleOfKeyFrameInterval !== this.lastMultipleOfKeyFrameInterval
|
|
32459
32650
|
};
|
|
32460
32651
|
this.lastMultipleOfKeyFrameInterval = multipleOfKeyFrameInterval;
|
|
32652
|
+
this.encodingConfig.onEncodedSample?.(sampleToEncode);
|
|
32461
32653
|
if (this.customEncoder) {
|
|
32462
32654
|
this.customEncoderQueueSize++;
|
|
32463
32655
|
const clonedSample = sampleToEncode.clone();
|
|
@@ -32712,6 +32904,7 @@ ${cue.notes ?? ""}`;
|
|
|
32712
32904
|
const alignedEnd = floorToDivisor(this.frameRateLastEndTimestamp, frameRate);
|
|
32713
32905
|
await this.padFrameRate(alignedEnd);
|
|
32714
32906
|
}
|
|
32907
|
+
this.closed = true;
|
|
32715
32908
|
this.frameRateLastSample?.close();
|
|
32716
32909
|
this.frameRateLastSample = null;
|
|
32717
32910
|
if (this.customEncoder) {
|
|
@@ -33585,6 +33778,7 @@ ${cue.notes ?? ""}`;
|
|
|
33585
33778
|
*/
|
|
33586
33779
|
this.error = null;
|
|
33587
33780
|
this.lastMuxerPromise = Promise.resolve();
|
|
33781
|
+
this.closed = false;
|
|
33588
33782
|
}
|
|
33589
33783
|
async add(audioSample, shouldClose) {
|
|
33590
33784
|
try {
|
|
@@ -33607,8 +33801,6 @@ ${cue.notes ?? ""}`;
|
|
|
33607
33801
|
this.resampler = new AudioResampler({
|
|
33608
33802
|
targetNumberOfChannels: config.transform.numberOfChannels ?? audioSample.numberOfChannels,
|
|
33609
33803
|
targetSampleRate: config.transform.sampleRate ?? audioSample.sampleRate,
|
|
33610
|
-
startTime: audioSample.timestamp,
|
|
33611
|
-
endTime: Infinity,
|
|
33612
33804
|
onSample: async (sample) => {
|
|
33613
33805
|
await this.processAndEncode(sample, true);
|
|
33614
33806
|
}
|
|
@@ -33677,6 +33869,9 @@ ${cue.notes ?? ""}`;
|
|
|
33677
33869
|
}
|
|
33678
33870
|
}
|
|
33679
33871
|
assert(this.encoderInitialized);
|
|
33872
|
+
if (this.closed) {
|
|
33873
|
+
return;
|
|
33874
|
+
}
|
|
33680
33875
|
{
|
|
33681
33876
|
const startSampleIndex = Math.round(
|
|
33682
33877
|
audioSample.timestamp * audioSample.sampleRate
|
|
@@ -33702,6 +33897,7 @@ ${cue.notes ?? ""}`;
|
|
|
33702
33897
|
this.lastEndSampleIndex += audioSample.numberOfFrames;
|
|
33703
33898
|
}
|
|
33704
33899
|
}
|
|
33900
|
+
this.encodingConfig.onEncodedSample?.(audioSample);
|
|
33705
33901
|
if (this.customEncoder) {
|
|
33706
33902
|
this.customEncoderQueueSize++;
|
|
33707
33903
|
const clonedSample = audioSample.clone();
|
|
@@ -33974,6 +34170,7 @@ ${cue.notes ?? ""}`;
|
|
|
33974
34170
|
await this.resampler.finalize();
|
|
33975
34171
|
}
|
|
33976
34172
|
this.resampler = null;
|
|
34173
|
+
this.closed = true;
|
|
33977
34174
|
if (this.customEncoder) {
|
|
33978
34175
|
if (!forceClose) {
|
|
33979
34176
|
void this.customEncoderCallSerializer.call(() => this.customEncoder.flush());
|
|
@@ -37336,6 +37533,9 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
37336
37533
|
if (trackOptions.frameRate) {
|
|
37337
37534
|
encodingConfig.transform.frameRate = trackOptions.frameRate;
|
|
37338
37535
|
}
|
|
37536
|
+
if (trackOptions.process) {
|
|
37537
|
+
encodingConfig.transform.process = trackOptions.process;
|
|
37538
|
+
}
|
|
37339
37539
|
if (needsRerender) {
|
|
37340
37540
|
outputTrackRotation = 0;
|
|
37341
37541
|
encodingConfig.transform.width = width;
|
|
@@ -37345,6 +37545,10 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
37345
37545
|
encodingConfig.transform.crop = crop;
|
|
37346
37546
|
encodingConfig.transform.alpha = alpha;
|
|
37347
37547
|
}
|
|
37548
|
+
let lastSampleTimestamp = null;
|
|
37549
|
+
encodingConfig.onEncodedSample = (sample) => {
|
|
37550
|
+
lastSampleTimestamp = sample.timestamp;
|
|
37551
|
+
};
|
|
37348
37552
|
const source = new VideoSampleSource(encodingConfig);
|
|
37349
37553
|
videoSource = source;
|
|
37350
37554
|
this._trackPromises.push((async () => {
|
|
@@ -37357,7 +37561,13 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
37357
37561
|
}
|
|
37358
37562
|
const adjustedSampleTimestamp = Math.max(sample.timestamp - this._startTimestamp, 0);
|
|
37359
37563
|
sample.setTimestamp(adjustedSampleTimestamp);
|
|
37360
|
-
|
|
37564
|
+
this._reportProgress(outputTrackId, sample.timestamp + sample.duration);
|
|
37565
|
+
await source.add(sample);
|
|
37566
|
+
if (lastSampleTimestamp !== null) {
|
|
37567
|
+
if (this._synchronizer.shouldWait(outputTrackId, lastSampleTimestamp)) {
|
|
37568
|
+
await this._synchronizer.wait(lastSampleTimestamp);
|
|
37569
|
+
}
|
|
37570
|
+
}
|
|
37361
37571
|
sample.close();
|
|
37362
37572
|
}
|
|
37363
37573
|
source.close();
|
|
@@ -37385,52 +37595,6 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
37385
37595
|
this._outputOwnTrackGroups.push(ownGroup);
|
|
37386
37596
|
}
|
|
37387
37597
|
/** @internal */
|
|
37388
|
-
async _registerVideoSample(trackOptions, outputTrackId, source, sample) {
|
|
37389
|
-
if (this._canceled) {
|
|
37390
|
-
return;
|
|
37391
|
-
}
|
|
37392
|
-
this._reportProgress(outputTrackId, sample.timestamp + sample.duration);
|
|
37393
|
-
let finalSamples;
|
|
37394
|
-
if (!trackOptions.process) {
|
|
37395
|
-
finalSamples = [sample];
|
|
37396
|
-
} else {
|
|
37397
|
-
let processed = trackOptions.process(sample);
|
|
37398
|
-
if (processed instanceof Promise) processed = await processed;
|
|
37399
|
-
if (!Array.isArray(processed)) {
|
|
37400
|
-
processed = processed === null ? [] : [processed];
|
|
37401
|
-
}
|
|
37402
|
-
finalSamples = processed.map((x) => {
|
|
37403
|
-
if (x instanceof VideoSample) {
|
|
37404
|
-
return x;
|
|
37405
|
-
}
|
|
37406
|
-
if (typeof VideoFrame !== "undefined" && x instanceof VideoFrame) {
|
|
37407
|
-
return new VideoSample(x);
|
|
37408
|
-
}
|
|
37409
|
-
return new VideoSample(x, {
|
|
37410
|
-
timestamp: sample.timestamp,
|
|
37411
|
-
duration: sample.duration
|
|
37412
|
-
});
|
|
37413
|
-
});
|
|
37414
|
-
}
|
|
37415
|
-
try {
|
|
37416
|
-
for (const finalSample of finalSamples) {
|
|
37417
|
-
if (this._canceled) {
|
|
37418
|
-
break;
|
|
37419
|
-
}
|
|
37420
|
-
await source.add(finalSample);
|
|
37421
|
-
if (this._synchronizer.shouldWait(outputTrackId, finalSample.timestamp)) {
|
|
37422
|
-
await this._synchronizer.wait(finalSample.timestamp);
|
|
37423
|
-
}
|
|
37424
|
-
}
|
|
37425
|
-
} finally {
|
|
37426
|
-
for (const finalSample of finalSamples) {
|
|
37427
|
-
if (finalSample !== sample) {
|
|
37428
|
-
finalSample.close();
|
|
37429
|
-
}
|
|
37430
|
-
}
|
|
37431
|
-
}
|
|
37432
|
-
}
|
|
37433
|
-
/** @internal */
|
|
37434
37598
|
async _processAudioTrack(track, trackOptions, outputTrackId) {
|
|
37435
37599
|
const sourceCodec = await track.getCodec();
|
|
37436
37600
|
if (!sourceCodec) {
|
|
@@ -37447,9 +37611,10 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
37447
37611
|
const firstTimestamp = await track.getFirstTimestamp();
|
|
37448
37612
|
let numberOfChannels = trackOptions.numberOfChannels ?? originalNumberOfChannels;
|
|
37449
37613
|
let sampleRate = trackOptions.sampleRate ?? originalSampleRate;
|
|
37450
|
-
|
|
37614
|
+
const needsTrimming = firstTimestamp < this._startTimestamp;
|
|
37615
|
+
const needsPadding = firstTimestamp > this._startTimestamp && !this.output.format.supportsTimestampedMediaData;
|
|
37451
37616
|
let audioCodecs = this.output.format.getSupportedAudioCodecs();
|
|
37452
|
-
if (!trackOptions.forceTranscode && !trackOptions.bitrate && !
|
|
37617
|
+
if (!trackOptions.forceTranscode && !trackOptions.bitrate && numberOfChannels === originalNumberOfChannels && sampleRate === originalSampleRate && !needsTrimming && !needsPadding && audioCodecs.includes(sourceCodec) && (!trackOptions.codec || trackOptions.codec === sourceCodec) && !trackOptions.process && trackOptions.sampleFormat === void 0) {
|
|
37453
37618
|
const source = new EncodedAudioPacketSource(sourceCodec);
|
|
37454
37619
|
audioSource = source;
|
|
37455
37620
|
this._trackPromises.push((async () => {
|
|
@@ -37505,7 +37670,6 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
37505
37670
|
});
|
|
37506
37671
|
const nonPcmCodec = encodableCodecsWithDefaultParams.find((codec) => NON_PCM_AUDIO_CODECS.includes(codec));
|
|
37507
37672
|
if (nonPcmCodec) {
|
|
37508
|
-
needsResample = true;
|
|
37509
37673
|
codecOfChoice = nonPcmCodec;
|
|
37510
37674
|
numberOfChannels = FALLBACK_NUMBER_OF_CHANNELS;
|
|
37511
37675
|
sampleRate = FALLBACK_SAMPLE_RATE;
|
|
@@ -37521,38 +37685,70 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
37521
37685
|
});
|
|
37522
37686
|
return;
|
|
37523
37687
|
}
|
|
37524
|
-
|
|
37525
|
-
|
|
37526
|
-
|
|
37527
|
-
|
|
37528
|
-
|
|
37529
|
-
|
|
37530
|
-
|
|
37531
|
-
|
|
37532
|
-
|
|
37533
|
-
|
|
37534
|
-
|
|
37535
|
-
|
|
37536
|
-
|
|
37537
|
-
|
|
37538
|
-
|
|
37539
|
-
|
|
37540
|
-
|
|
37541
|
-
|
|
37542
|
-
|
|
37543
|
-
|
|
37544
|
-
|
|
37688
|
+
const encodingConfig = {
|
|
37689
|
+
codec: codecOfChoice,
|
|
37690
|
+
bitrate,
|
|
37691
|
+
transform: {
|
|
37692
|
+
sampleFormat: trackOptions.sampleFormat,
|
|
37693
|
+
process: trackOptions.process
|
|
37694
|
+
}
|
|
37695
|
+
};
|
|
37696
|
+
assert(encodingConfig.transform);
|
|
37697
|
+
if (numberOfChannels !== originalNumberOfChannels) {
|
|
37698
|
+
encodingConfig.transform.numberOfChannels = numberOfChannels;
|
|
37699
|
+
}
|
|
37700
|
+
if (sampleRate !== originalSampleRate) {
|
|
37701
|
+
encodingConfig.transform.sampleRate = sampleRate;
|
|
37702
|
+
}
|
|
37703
|
+
let lastSampleTimestamp = null;
|
|
37704
|
+
encodingConfig.onEncodedSample = (sample) => {
|
|
37705
|
+
lastSampleTimestamp = sample.timestamp;
|
|
37706
|
+
};
|
|
37707
|
+
const source = new AudioSampleSource(encodingConfig);
|
|
37708
|
+
audioSource = source;
|
|
37709
|
+
this._trackPromises.push((async () => {
|
|
37710
|
+
await this._started;
|
|
37711
|
+
if (needsPadding) {
|
|
37712
|
+
const paddingLength = firstTimestamp - this._startTimestamp;
|
|
37713
|
+
const paddingLengthSamples = Math.round(paddingLength * originalSampleRate);
|
|
37714
|
+
const silentSample = new AudioSample({
|
|
37715
|
+
data: new Float32Array(paddingLengthSamples * originalNumberOfChannels),
|
|
37716
|
+
format: "f32-planar",
|
|
37717
|
+
numberOfChannels: originalNumberOfChannels,
|
|
37718
|
+
sampleRate: originalSampleRate,
|
|
37719
|
+
timestamp: 0
|
|
37720
|
+
});
|
|
37721
|
+
await this._registerAudioSample(silentSample, source, outputTrackId, () => lastSampleTimestamp);
|
|
37722
|
+
}
|
|
37723
|
+
const sink = new AudioSampleSink(track);
|
|
37724
|
+
for await (let sample of sink.samples(this._startTimestamp, this._endTimestamp)) {
|
|
37725
|
+
if (this._canceled) {
|
|
37726
|
+
sample.close();
|
|
37727
|
+
return;
|
|
37728
|
+
}
|
|
37729
|
+
let startFrame = 0;
|
|
37730
|
+
let endFrame = sample.numberOfFrames;
|
|
37731
|
+
if (sample.timestamp < this._startTimestamp) {
|
|
37732
|
+
startFrame = Math.round((this._startTimestamp - sample.timestamp) * sample.sampleRate);
|
|
37733
|
+
}
|
|
37734
|
+
if (sample.timestamp + sample.duration > this._endTimestamp) {
|
|
37735
|
+
endFrame = Math.round((this._endTimestamp - sample.timestamp) * sample.sampleRate);
|
|
37736
|
+
}
|
|
37737
|
+
if (startFrame > 0 || endFrame < sample.numberOfFrames) {
|
|
37738
|
+
const trimmedSample = sample.trim(startFrame, endFrame);
|
|
37739
|
+
sample.close();
|
|
37740
|
+
sample = trimmedSample;
|
|
37741
|
+
if (sample.numberOfFrames === 0) {
|
|
37545
37742
|
sample.close();
|
|
37546
|
-
|
|
37743
|
+
continue;
|
|
37547
37744
|
}
|
|
37548
|
-
sample.setTimestamp(sample.timestamp - this._startTimestamp);
|
|
37549
|
-
await this._registerAudioSample(trackOptions, outputTrackId, source, sample);
|
|
37550
|
-
sample.close();
|
|
37551
37745
|
}
|
|
37552
|
-
|
|
37553
|
-
this.
|
|
37554
|
-
}
|
|
37555
|
-
|
|
37746
|
+
sample.setTimestamp(sample.timestamp - this._startTimestamp);
|
|
37747
|
+
await this._registerAudioSample(sample, source, outputTrackId, () => lastSampleTimestamp);
|
|
37748
|
+
}
|
|
37749
|
+
source.close();
|
|
37750
|
+
this._synchronizer.closeTrack(outputTrackId);
|
|
37751
|
+
})());
|
|
37556
37752
|
}
|
|
37557
37753
|
let ownGroup = null;
|
|
37558
37754
|
if (!trackOptions.group) {
|
|
@@ -37573,89 +37769,18 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
37573
37769
|
this._outputOwnTrackGroups.push(ownGroup);
|
|
37574
37770
|
}
|
|
37575
37771
|
/** @internal */
|
|
37576
|
-
async _registerAudioSample(
|
|
37577
|
-
if (this._canceled) {
|
|
37578
|
-
return;
|
|
37579
|
-
}
|
|
37580
|
-
let sample = inputSample;
|
|
37581
|
-
if (trackOptions.sampleFormat !== void 0 && toInterleavedAudioFormat(sample.format) !== trackOptions.sampleFormat) {
|
|
37582
|
-
sample = audioSampleToInterleavedFormat(sample, trackOptions.sampleFormat);
|
|
37583
|
-
}
|
|
37772
|
+
async _registerAudioSample(sample, source, outputTrackId, getLastSampleTimestamp) {
|
|
37584
37773
|
this._reportProgress(outputTrackId, sample.timestamp + sample.duration);
|
|
37585
|
-
|
|
37586
|
-
|
|
37587
|
-
|
|
37588
|
-
|
|
37589
|
-
|
|
37590
|
-
|
|
37591
|
-
if (!Array.isArray(processed)) {
|
|
37592
|
-
processed = processed === null ? [] : [processed];
|
|
37593
|
-
}
|
|
37594
|
-
if (!processed.every((x) => x instanceof AudioSample)) {
|
|
37595
|
-
throw new TypeError(
|
|
37596
|
-
"The audio process function must return an AudioSample, null, or an array of AudioSamples."
|
|
37597
|
-
);
|
|
37598
|
-
}
|
|
37599
|
-
finalSamples = processed;
|
|
37600
|
-
}
|
|
37601
|
-
try {
|
|
37602
|
-
for (const finalSample of finalSamples) {
|
|
37603
|
-
if (this._canceled) {
|
|
37604
|
-
break;
|
|
37605
|
-
}
|
|
37606
|
-
await source.add(finalSample);
|
|
37607
|
-
if (this._synchronizer.shouldWait(outputTrackId, finalSample.timestamp)) {
|
|
37608
|
-
await this._synchronizer.wait(finalSample.timestamp);
|
|
37609
|
-
}
|
|
37610
|
-
}
|
|
37611
|
-
} finally {
|
|
37612
|
-
if (sample !== inputSample) {
|
|
37613
|
-
sample.close();
|
|
37614
|
-
}
|
|
37615
|
-
for (const finalSample of finalSamples) {
|
|
37616
|
-
if (finalSample !== inputSample) {
|
|
37617
|
-
finalSample.close();
|
|
37618
|
-
}
|
|
37774
|
+
await source.add(sample);
|
|
37775
|
+
sample.close();
|
|
37776
|
+
const lastSampleTimestamp = getLastSampleTimestamp();
|
|
37777
|
+
if (lastSampleTimestamp !== null) {
|
|
37778
|
+
if (this._synchronizer.shouldWait(outputTrackId, lastSampleTimestamp)) {
|
|
37779
|
+
await this._synchronizer.wait(lastSampleTimestamp);
|
|
37619
37780
|
}
|
|
37620
37781
|
}
|
|
37621
37782
|
}
|
|
37622
37783
|
/** @internal */
|
|
37623
|
-
_resampleAudio(track, trackOptions, outputTrackId, codec, targetNumberOfChannels, targetSampleRate, bitrate) {
|
|
37624
|
-
const source = new AudioSampleSource({
|
|
37625
|
-
codec,
|
|
37626
|
-
bitrate
|
|
37627
|
-
});
|
|
37628
|
-
this._trackPromises.push((async () => {
|
|
37629
|
-
await this._started;
|
|
37630
|
-
const resampler = new AudioResampler({
|
|
37631
|
-
targetNumberOfChannels,
|
|
37632
|
-
targetSampleRate,
|
|
37633
|
-
startTime: this._startTimestamp,
|
|
37634
|
-
endTime: this._endTimestamp,
|
|
37635
|
-
onSample: async (sample) => {
|
|
37636
|
-
assert(sample.timestamp >= this._startTimestamp);
|
|
37637
|
-
sample.setTimestamp(sample.timestamp - this._startTimestamp);
|
|
37638
|
-
await this._registerAudioSample(trackOptions, outputTrackId, source, sample);
|
|
37639
|
-
sample.close();
|
|
37640
|
-
}
|
|
37641
|
-
});
|
|
37642
|
-
const sink = new AudioSampleSink(track);
|
|
37643
|
-
const iterator = sink.samples(this._startTimestamp, this._endTimestamp);
|
|
37644
|
-
for await (const sample of iterator) {
|
|
37645
|
-
if (this._canceled) {
|
|
37646
|
-
sample.close();
|
|
37647
|
-
return;
|
|
37648
|
-
}
|
|
37649
|
-
await resampler.add(sample);
|
|
37650
|
-
sample.close();
|
|
37651
|
-
}
|
|
37652
|
-
await resampler.finalize();
|
|
37653
|
-
source.close();
|
|
37654
|
-
this._synchronizer.closeTrack(outputTrackId);
|
|
37655
|
-
})());
|
|
37656
|
-
return source;
|
|
37657
|
-
}
|
|
37658
|
-
/** @internal */
|
|
37659
37784
|
_reportProgress(trackId, endTimestamp) {
|
|
37660
37785
|
if (!this._computeProgress) {
|
|
37661
37786
|
return;
|