mediabunny 1.4.3 → 1.5.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 +416 -188
- package/dist/bundles/mediabunny.min.cjs +5 -5
- package/dist/bundles/mediabunny.min.mjs +4 -4
- package/dist/bundles/mediabunny.mjs +416 -188
- package/dist/mediabunny.d.ts +5 -0
- package/dist/modules/conversion.d.ts +5 -0
- package/dist/modules/conversion.d.ts.map +1 -1
- package/dist/modules/conversion.js +117 -11
- package/dist/modules/isobmff/isobmff-demuxer.js +132 -129
- package/dist/modules/matroska/ebml.d.ts +4 -0
- package/dist/modules/matroska/ebml.d.ts.map +1 -1
- package/dist/modules/matroska/ebml.js +31 -1
- package/dist/modules/matroska/matroska-demuxer.d.ts +9 -0
- package/dist/modules/matroska/matroska-demuxer.d.ts.map +1 -1
- package/dist/modules/matroska/matroska-demuxer.js +148 -10
- package/package.json +1 -1
- package/src/conversion.ts +147 -14
- package/src/isobmff/isobmff-demuxer.ts +188 -181
- package/src/matroska/ebml.ts +39 -1
- package/src/matroska/matroska-demuxer.ts +165 -9
|
@@ -5660,7 +5660,11 @@ var Mediabunny = (() => {
|
|
|
5660
5660
|
readString(length) {
|
|
5661
5661
|
const { view: view2, offset } = this.reader.getViewAndOffset(this.pos, this.pos + length);
|
|
5662
5662
|
this.pos += length;
|
|
5663
|
-
|
|
5663
|
+
let strLength = 0;
|
|
5664
|
+
while (strLength < length && view2.getUint8(offset + strLength) !== 0) {
|
|
5665
|
+
strLength += 1;
|
|
5666
|
+
}
|
|
5667
|
+
return String.fromCharCode(...new Uint8Array(view2.buffer, offset, strLength));
|
|
5664
5668
|
}
|
|
5665
5669
|
readElementId() {
|
|
5666
5670
|
const size = this.readVarIntSize();
|
|
@@ -5726,6 +5730,27 @@ var Mediabunny = (() => {
|
|
|
5726
5730
|
"pcm-f64": "A_PCM/FLOAT/IEEE",
|
|
5727
5731
|
"webvtt": "S_TEXT/WEBVTT"
|
|
5728
5732
|
};
|
|
5733
|
+
var readVarInt = (data, offset) => {
|
|
5734
|
+
if (offset >= data.length) {
|
|
5735
|
+
throw new Error("Offset out of bounds.");
|
|
5736
|
+
}
|
|
5737
|
+
const firstByte = data[offset];
|
|
5738
|
+
let width = 1;
|
|
5739
|
+
let mask = 1 << 7;
|
|
5740
|
+
while ((firstByte & mask) === 0 && width < 8) {
|
|
5741
|
+
width++;
|
|
5742
|
+
mask >>= 1;
|
|
5743
|
+
}
|
|
5744
|
+
if (offset + width > data.length) {
|
|
5745
|
+
throw new Error("VarInt extends beyond data bounds.");
|
|
5746
|
+
}
|
|
5747
|
+
let value = firstByte & mask - 1;
|
|
5748
|
+
for (let i = 1; i < width; i++) {
|
|
5749
|
+
value *= 1 << 8;
|
|
5750
|
+
value += data[offset + i];
|
|
5751
|
+
}
|
|
5752
|
+
return { value, width };
|
|
5753
|
+
};
|
|
5729
5754
|
function assertDefinedSize(size) {
|
|
5730
5755
|
if (size === null) {
|
|
5731
5756
|
throw new Error("Undefined element size is used in a place where it is not supported.");
|
|
@@ -13571,199 +13596,195 @@ ${cue.notes ?? ""}`;
|
|
|
13571
13596
|
return firstPacket?.timestamp ?? 0;
|
|
13572
13597
|
}
|
|
13573
13598
|
async getFirstPacket(options) {
|
|
13574
|
-
|
|
13575
|
-
|
|
13576
|
-
|
|
13577
|
-
|
|
13578
|
-
|
|
13579
|
-
|
|
13580
|
-
|
|
13581
|
-
|
|
13582
|
-
|
|
13583
|
-
|
|
13584
|
-
|
|
13585
|
-
|
|
13586
|
-
|
|
13587
|
-
|
|
13588
|
-
|
|
13589
|
-
|
|
13590
|
-
|
|
13591
|
-
|
|
13592
|
-
|
|
13593
|
-
|
|
13599
|
+
const regularPacket = await this.fetchPacketForSampleIndex(0, options);
|
|
13600
|
+
if (regularPacket || !this.internalTrack.demuxer.isFragmented) {
|
|
13601
|
+
return regularPacket;
|
|
13602
|
+
}
|
|
13603
|
+
return this.performFragmentedLookup(
|
|
13604
|
+
() => {
|
|
13605
|
+
const startFragment = this.internalTrack.demuxer.fragments[0] ?? null;
|
|
13606
|
+
if (startFragment?.isKnownToBeFirstFragment) {
|
|
13607
|
+
let currentFragment = startFragment;
|
|
13608
|
+
while (currentFragment) {
|
|
13609
|
+
const trackData = currentFragment.trackData.get(this.internalTrack.id);
|
|
13610
|
+
if (trackData) {
|
|
13611
|
+
return {
|
|
13612
|
+
fragmentIndex: binarySearchExact(
|
|
13613
|
+
this.internalTrack.fragments,
|
|
13614
|
+
currentFragment.moofOffset,
|
|
13615
|
+
(x) => x.moofOffset
|
|
13616
|
+
),
|
|
13617
|
+
sampleIndex: 0,
|
|
13618
|
+
correctSampleFound: true
|
|
13619
|
+
};
|
|
13594
13620
|
}
|
|
13621
|
+
currentFragment = currentFragment.nextFragment;
|
|
13595
13622
|
}
|
|
13596
|
-
|
|
13597
|
-
|
|
13598
|
-
|
|
13599
|
-
|
|
13600
|
-
|
|
13601
|
-
}
|
|
13602
|
-
|
|
13603
|
-
|
|
13604
|
-
|
|
13605
|
-
|
|
13606
|
-
|
|
13607
|
-
|
|
13608
|
-
return this.fetchPacketForSampleIndex(0, options);
|
|
13623
|
+
}
|
|
13624
|
+
return {
|
|
13625
|
+
fragmentIndex: -1,
|
|
13626
|
+
sampleIndex: -1,
|
|
13627
|
+
correctSampleFound: false
|
|
13628
|
+
};
|
|
13629
|
+
},
|
|
13630
|
+
-Infinity,
|
|
13631
|
+
// Use -Infinity as a search timestamp to avoid using the lookup entries
|
|
13632
|
+
Infinity,
|
|
13633
|
+
options
|
|
13634
|
+
);
|
|
13609
13635
|
}
|
|
13610
13636
|
mapTimestampIntoTimescale(timestamp) {
|
|
13611
13637
|
return roundToPrecision(timestamp * this.internalTrack.timescale, 14) + this.internalTrack.editListOffset;
|
|
13612
13638
|
}
|
|
13613
13639
|
async getPacket(timestamp, options) {
|
|
13614
13640
|
const timestampInTimescale = this.mapTimestampIntoTimescale(timestamp);
|
|
13615
|
-
|
|
13616
|
-
|
|
13617
|
-
|
|
13618
|
-
|
|
13619
|
-
|
|
13620
|
-
options
|
|
13621
|
-
);
|
|
13622
|
-
} else {
|
|
13623
|
-
const sampleTable = this.internalTrack.demuxer.getSampleTableForTrack(this.internalTrack);
|
|
13624
|
-
const sampleIndex = getSampleIndexForTimestamp(sampleTable, timestampInTimescale);
|
|
13625
|
-
return this.fetchPacketForSampleIndex(sampleIndex, options);
|
|
13641
|
+
const sampleTable = this.internalTrack.demuxer.getSampleTableForTrack(this.internalTrack);
|
|
13642
|
+
const sampleIndex = getSampleIndexForTimestamp(sampleTable, timestampInTimescale);
|
|
13643
|
+
const regularPacket = await this.fetchPacketForSampleIndex(sampleIndex, options);
|
|
13644
|
+
if (!sampleTableIsEmpty(sampleTable) || !this.internalTrack.demuxer.isFragmented) {
|
|
13645
|
+
return regularPacket;
|
|
13626
13646
|
}
|
|
13647
|
+
return this.performFragmentedLookup(
|
|
13648
|
+
() => this.findSampleInFragmentsForTimestamp(timestampInTimescale),
|
|
13649
|
+
timestampInTimescale,
|
|
13650
|
+
timestampInTimescale,
|
|
13651
|
+
options
|
|
13652
|
+
);
|
|
13627
13653
|
}
|
|
13628
13654
|
async getNextPacket(packet, options) {
|
|
13629
|
-
|
|
13630
|
-
|
|
13631
|
-
|
|
13632
|
-
throw new Error("Packet was not created from this track.");
|
|
13633
|
-
}
|
|
13634
|
-
const trackData = locationInFragment.fragment.trackData.get(this.internalTrack.id);
|
|
13635
|
-
const fragmentSample = trackData.samples[locationInFragment.sampleIndex];
|
|
13636
|
-
const fragmentIndex = binarySearchExact(
|
|
13637
|
-
this.internalTrack.fragments,
|
|
13638
|
-
locationInFragment.fragment.moofOffset,
|
|
13639
|
-
(x) => x.moofOffset
|
|
13640
|
-
);
|
|
13641
|
-
assert(fragmentIndex !== -1);
|
|
13642
|
-
return this.performFragmentedLookup(
|
|
13643
|
-
() => {
|
|
13644
|
-
if (locationInFragment.sampleIndex + 1 < trackData.samples.length) {
|
|
13645
|
-
return {
|
|
13646
|
-
fragmentIndex,
|
|
13647
|
-
sampleIndex: locationInFragment.sampleIndex + 1,
|
|
13648
|
-
correctSampleFound: true
|
|
13649
|
-
};
|
|
13650
|
-
} else {
|
|
13651
|
-
let currentFragment = locationInFragment.fragment;
|
|
13652
|
-
while (currentFragment.nextFragment) {
|
|
13653
|
-
currentFragment = currentFragment.nextFragment;
|
|
13654
|
-
const trackData2 = currentFragment.trackData.get(this.internalTrack.id);
|
|
13655
|
-
if (trackData2) {
|
|
13656
|
-
const fragmentIndex2 = binarySearchExact(
|
|
13657
|
-
this.internalTrack.fragments,
|
|
13658
|
-
currentFragment.moofOffset,
|
|
13659
|
-
(x) => x.moofOffset
|
|
13660
|
-
);
|
|
13661
|
-
assert(fragmentIndex2 !== -1);
|
|
13662
|
-
return {
|
|
13663
|
-
fragmentIndex: fragmentIndex2,
|
|
13664
|
-
sampleIndex: 0,
|
|
13665
|
-
correctSampleFound: true
|
|
13666
|
-
};
|
|
13667
|
-
}
|
|
13668
|
-
}
|
|
13669
|
-
return {
|
|
13670
|
-
fragmentIndex,
|
|
13671
|
-
sampleIndex: -1,
|
|
13672
|
-
correctSampleFound: false
|
|
13673
|
-
};
|
|
13674
|
-
}
|
|
13675
|
-
},
|
|
13676
|
-
fragmentSample.presentationTimestamp,
|
|
13677
|
-
Infinity,
|
|
13678
|
-
options
|
|
13679
|
-
);
|
|
13655
|
+
const regularSampleIndex = this.packetToSampleIndex.get(packet);
|
|
13656
|
+
if (regularSampleIndex !== void 0) {
|
|
13657
|
+
return this.fetchPacketForSampleIndex(regularSampleIndex + 1, options);
|
|
13680
13658
|
}
|
|
13681
|
-
const
|
|
13682
|
-
if (
|
|
13659
|
+
const locationInFragment = this.packetToFragmentLocation.get(packet);
|
|
13660
|
+
if (locationInFragment === void 0) {
|
|
13683
13661
|
throw new Error("Packet was not created from this track.");
|
|
13684
13662
|
}
|
|
13685
|
-
|
|
13663
|
+
const trackData = locationInFragment.fragment.trackData.get(this.internalTrack.id);
|
|
13664
|
+
const fragmentIndex = binarySearchExact(
|
|
13665
|
+
this.internalTrack.fragments,
|
|
13666
|
+
locationInFragment.fragment.moofOffset,
|
|
13667
|
+
(x) => x.moofOffset
|
|
13668
|
+
);
|
|
13669
|
+
assert(fragmentIndex !== -1);
|
|
13670
|
+
return this.performFragmentedLookup(
|
|
13671
|
+
() => {
|
|
13672
|
+
if (locationInFragment.sampleIndex + 1 < trackData.samples.length) {
|
|
13673
|
+
return {
|
|
13674
|
+
fragmentIndex,
|
|
13675
|
+
sampleIndex: locationInFragment.sampleIndex + 1,
|
|
13676
|
+
correctSampleFound: true
|
|
13677
|
+
};
|
|
13678
|
+
} else {
|
|
13679
|
+
let currentFragment = locationInFragment.fragment;
|
|
13680
|
+
while (currentFragment.nextFragment) {
|
|
13681
|
+
currentFragment = currentFragment.nextFragment;
|
|
13682
|
+
const trackData2 = currentFragment.trackData.get(this.internalTrack.id);
|
|
13683
|
+
if (trackData2) {
|
|
13684
|
+
const fragmentIndex2 = binarySearchExact(
|
|
13685
|
+
this.internalTrack.fragments,
|
|
13686
|
+
currentFragment.moofOffset,
|
|
13687
|
+
(x) => x.moofOffset
|
|
13688
|
+
);
|
|
13689
|
+
assert(fragmentIndex2 !== -1);
|
|
13690
|
+
return {
|
|
13691
|
+
fragmentIndex: fragmentIndex2,
|
|
13692
|
+
sampleIndex: 0,
|
|
13693
|
+
correctSampleFound: true
|
|
13694
|
+
};
|
|
13695
|
+
}
|
|
13696
|
+
}
|
|
13697
|
+
return {
|
|
13698
|
+
fragmentIndex,
|
|
13699
|
+
sampleIndex: -1,
|
|
13700
|
+
correctSampleFound: false
|
|
13701
|
+
};
|
|
13702
|
+
}
|
|
13703
|
+
},
|
|
13704
|
+
-Infinity,
|
|
13705
|
+
// Use -Infinity as a search timestamp to avoid using the lookup entries
|
|
13706
|
+
Infinity,
|
|
13707
|
+
options
|
|
13708
|
+
);
|
|
13686
13709
|
}
|
|
13687
13710
|
async getKeyPacket(timestamp, options) {
|
|
13688
13711
|
const timestampInTimescale = this.mapTimestampIntoTimescale(timestamp);
|
|
13689
|
-
if (this.internalTrack.demuxer.isFragmented) {
|
|
13690
|
-
return this.performFragmentedLookup(
|
|
13691
|
-
() => this.findKeySampleInFragmentsForTimestamp(timestampInTimescale),
|
|
13692
|
-
timestampInTimescale,
|
|
13693
|
-
timestampInTimescale,
|
|
13694
|
-
options
|
|
13695
|
-
);
|
|
13696
|
-
}
|
|
13697
13712
|
const sampleTable = this.internalTrack.demuxer.getSampleTableForTrack(this.internalTrack);
|
|
13698
13713
|
const sampleIndex = getSampleIndexForTimestamp(sampleTable, timestampInTimescale);
|
|
13699
13714
|
const keyFrameSampleIndex = sampleIndex === -1 ? -1 : getRelevantKeyframeIndexForSample(sampleTable, sampleIndex);
|
|
13700
|
-
|
|
13715
|
+
const regularPacket = await this.fetchPacketForSampleIndex(keyFrameSampleIndex, options);
|
|
13716
|
+
if (!sampleTableIsEmpty(sampleTable) || !this.internalTrack.demuxer.isFragmented) {
|
|
13717
|
+
return regularPacket;
|
|
13718
|
+
}
|
|
13719
|
+
return this.performFragmentedLookup(
|
|
13720
|
+
() => this.findKeySampleInFragmentsForTimestamp(timestampInTimescale),
|
|
13721
|
+
timestampInTimescale,
|
|
13722
|
+
timestampInTimescale,
|
|
13723
|
+
options
|
|
13724
|
+
);
|
|
13701
13725
|
}
|
|
13702
13726
|
async getNextKeyPacket(packet, options) {
|
|
13703
|
-
|
|
13704
|
-
|
|
13705
|
-
|
|
13706
|
-
|
|
13707
|
-
|
|
13708
|
-
const trackData = locationInFragment.fragment.trackData.get(this.internalTrack.id);
|
|
13709
|
-
const fragmentSample = trackData.samples[locationInFragment.sampleIndex];
|
|
13710
|
-
const fragmentIndex = binarySearchExact(
|
|
13711
|
-
this.internalTrack.fragments,
|
|
13712
|
-
locationInFragment.fragment.moofOffset,
|
|
13713
|
-
(x) => x.moofOffset
|
|
13714
|
-
);
|
|
13715
|
-
assert(fragmentIndex !== -1);
|
|
13716
|
-
return this.performFragmentedLookup(
|
|
13717
|
-
() => {
|
|
13718
|
-
const nextKeyFrameIndex = trackData.samples.findIndex(
|
|
13719
|
-
(x, i) => x.isKeyFrame && i > locationInFragment.sampleIndex
|
|
13720
|
-
);
|
|
13721
|
-
if (nextKeyFrameIndex !== -1) {
|
|
13722
|
-
return {
|
|
13723
|
-
fragmentIndex,
|
|
13724
|
-
sampleIndex: nextKeyFrameIndex,
|
|
13725
|
-
correctSampleFound: true
|
|
13726
|
-
};
|
|
13727
|
-
} else {
|
|
13728
|
-
let currentFragment = locationInFragment.fragment;
|
|
13729
|
-
while (currentFragment.nextFragment) {
|
|
13730
|
-
currentFragment = currentFragment.nextFragment;
|
|
13731
|
-
const trackData2 = currentFragment.trackData.get(this.internalTrack.id);
|
|
13732
|
-
if (trackData2 && trackData2.firstKeyFrameTimestamp !== null) {
|
|
13733
|
-
const fragmentIndex2 = binarySearchExact(
|
|
13734
|
-
this.internalTrack.fragments,
|
|
13735
|
-
currentFragment.moofOffset,
|
|
13736
|
-
(x) => x.moofOffset
|
|
13737
|
-
);
|
|
13738
|
-
assert(fragmentIndex2 !== -1);
|
|
13739
|
-
const keyFrameIndex = trackData2.samples.findIndex((x) => x.isKeyFrame);
|
|
13740
|
-
assert(keyFrameIndex !== -1);
|
|
13741
|
-
return {
|
|
13742
|
-
fragmentIndex: fragmentIndex2,
|
|
13743
|
-
sampleIndex: keyFrameIndex,
|
|
13744
|
-
correctSampleFound: true
|
|
13745
|
-
};
|
|
13746
|
-
}
|
|
13747
|
-
}
|
|
13748
|
-
return {
|
|
13749
|
-
fragmentIndex,
|
|
13750
|
-
sampleIndex: -1,
|
|
13751
|
-
correctSampleFound: false
|
|
13752
|
-
};
|
|
13753
|
-
}
|
|
13754
|
-
},
|
|
13755
|
-
fragmentSample.presentationTimestamp,
|
|
13756
|
-
Infinity,
|
|
13757
|
-
options
|
|
13758
|
-
);
|
|
13727
|
+
const regularSampleIndex = this.packetToSampleIndex.get(packet);
|
|
13728
|
+
if (regularSampleIndex !== void 0) {
|
|
13729
|
+
const sampleTable = this.internalTrack.demuxer.getSampleTableForTrack(this.internalTrack);
|
|
13730
|
+
const nextKeyFrameSampleIndex = getNextKeyframeIndexForSample(sampleTable, regularSampleIndex);
|
|
13731
|
+
return this.fetchPacketForSampleIndex(nextKeyFrameSampleIndex, options);
|
|
13759
13732
|
}
|
|
13760
|
-
const
|
|
13761
|
-
if (
|
|
13733
|
+
const locationInFragment = this.packetToFragmentLocation.get(packet);
|
|
13734
|
+
if (locationInFragment === void 0) {
|
|
13762
13735
|
throw new Error("Packet was not created from this track.");
|
|
13763
13736
|
}
|
|
13764
|
-
const
|
|
13765
|
-
const
|
|
13766
|
-
|
|
13737
|
+
const trackData = locationInFragment.fragment.trackData.get(this.internalTrack.id);
|
|
13738
|
+
const fragmentIndex = binarySearchExact(
|
|
13739
|
+
this.internalTrack.fragments,
|
|
13740
|
+
locationInFragment.fragment.moofOffset,
|
|
13741
|
+
(x) => x.moofOffset
|
|
13742
|
+
);
|
|
13743
|
+
assert(fragmentIndex !== -1);
|
|
13744
|
+
return this.performFragmentedLookup(
|
|
13745
|
+
() => {
|
|
13746
|
+
const nextKeyFrameIndex = trackData.samples.findIndex(
|
|
13747
|
+
(x, i) => x.isKeyFrame && i > locationInFragment.sampleIndex
|
|
13748
|
+
);
|
|
13749
|
+
if (nextKeyFrameIndex !== -1) {
|
|
13750
|
+
return {
|
|
13751
|
+
fragmentIndex,
|
|
13752
|
+
sampleIndex: nextKeyFrameIndex,
|
|
13753
|
+
correctSampleFound: true
|
|
13754
|
+
};
|
|
13755
|
+
} else {
|
|
13756
|
+
let currentFragment = locationInFragment.fragment;
|
|
13757
|
+
while (currentFragment.nextFragment) {
|
|
13758
|
+
currentFragment = currentFragment.nextFragment;
|
|
13759
|
+
const trackData2 = currentFragment.trackData.get(this.internalTrack.id);
|
|
13760
|
+
if (trackData2 && trackData2.firstKeyFrameTimestamp !== null) {
|
|
13761
|
+
const fragmentIndex2 = binarySearchExact(
|
|
13762
|
+
this.internalTrack.fragments,
|
|
13763
|
+
currentFragment.moofOffset,
|
|
13764
|
+
(x) => x.moofOffset
|
|
13765
|
+
);
|
|
13766
|
+
assert(fragmentIndex2 !== -1);
|
|
13767
|
+
const keyFrameIndex = trackData2.samples.findIndex((x) => x.isKeyFrame);
|
|
13768
|
+
assert(keyFrameIndex !== -1);
|
|
13769
|
+
return {
|
|
13770
|
+
fragmentIndex: fragmentIndex2,
|
|
13771
|
+
sampleIndex: keyFrameIndex,
|
|
13772
|
+
correctSampleFound: true
|
|
13773
|
+
};
|
|
13774
|
+
}
|
|
13775
|
+
}
|
|
13776
|
+
return {
|
|
13777
|
+
fragmentIndex,
|
|
13778
|
+
sampleIndex: -1,
|
|
13779
|
+
correctSampleFound: false
|
|
13780
|
+
};
|
|
13781
|
+
}
|
|
13782
|
+
},
|
|
13783
|
+
-Infinity,
|
|
13784
|
+
// Use -Infinity as a search timestamp to avoid using the lookup entries
|
|
13785
|
+
Infinity,
|
|
13786
|
+
options
|
|
13787
|
+
);
|
|
13767
13788
|
}
|
|
13768
13789
|
async fetchPacketForSampleIndex(sampleIndex, options) {
|
|
13769
13790
|
if (sampleIndex === -1) {
|
|
@@ -14159,6 +14180,9 @@ ${cue.notes ?? ""}`;
|
|
|
14159
14180
|
const sinTheta = m21 / scaleX;
|
|
14160
14181
|
return -Math.atan2(sinTheta, cosTheta) * (180 / Math.PI);
|
|
14161
14182
|
};
|
|
14183
|
+
var sampleTableIsEmpty = (sampleTable) => {
|
|
14184
|
+
return sampleTable.sampleSizes.length === 0;
|
|
14185
|
+
};
|
|
14162
14186
|
|
|
14163
14187
|
// src/matroska/matroska-demuxer.ts
|
|
14164
14188
|
var METADATA_ELEMENTS = [
|
|
@@ -14402,12 +14426,15 @@ ${cue.notes ?? ""}`;
|
|
|
14402
14426
|
this.currentCluster = cluster;
|
|
14403
14427
|
this.readContiguousElements(this.clusterReader, size);
|
|
14404
14428
|
for (const [trackId, trackData] of cluster.trackData) {
|
|
14405
|
-
|
|
14429
|
+
const track = segment.tracks.find((x) => x.id === trackId) ?? null;
|
|
14406
14430
|
assert(trackData.blocks.length > 0);
|
|
14431
|
+
let blockReferencesExist = false;
|
|
14432
|
+
let hasLacedBlocks = false;
|
|
14407
14433
|
for (let i = 0; i < trackData.blocks.length; i++) {
|
|
14408
14434
|
const block = trackData.blocks[i];
|
|
14409
14435
|
block.timestamp += cluster.timestamp;
|
|
14410
14436
|
blockReferencesExist ||= block.referencedTimestamps.length > 0;
|
|
14437
|
+
hasLacedBlocks ||= block.lacing !== 0 /* None */;
|
|
14411
14438
|
}
|
|
14412
14439
|
if (blockReferencesExist) {
|
|
14413
14440
|
trackData.blocks = sortBlocksByReferences(trackData.blocks);
|
|
@@ -14422,13 +14449,23 @@ ${cue.notes ?? ""}`;
|
|
|
14422
14449
|
if (i < trackData.presentationTimestamps.length - 1) {
|
|
14423
14450
|
const nextEntry = trackData.presentationTimestamps[i + 1];
|
|
14424
14451
|
currentBlock.duration = nextEntry.timestamp - currentBlock.timestamp;
|
|
14452
|
+
} else if (currentBlock.duration === 0) {
|
|
14453
|
+
if (track?.defaultDuration != null) {
|
|
14454
|
+
if (currentBlock.lacing === 0 /* None */) {
|
|
14455
|
+
currentBlock.duration = track.defaultDuration;
|
|
14456
|
+
} else {
|
|
14457
|
+
}
|
|
14458
|
+
}
|
|
14425
14459
|
}
|
|
14426
14460
|
}
|
|
14461
|
+
if (hasLacedBlocks) {
|
|
14462
|
+
this.expandLacedBlocks(trackData.blocks, track);
|
|
14463
|
+
trackData.presentationTimestamps = trackData.blocks.map((block, i) => ({ timestamp: block.timestamp, blockIndex: i })).sort((a, b) => a.timestamp - b.timestamp);
|
|
14464
|
+
}
|
|
14427
14465
|
const firstBlock = trackData.blocks[trackData.presentationTimestamps[0].blockIndex];
|
|
14428
14466
|
const lastBlock = trackData.blocks[last(trackData.presentationTimestamps).blockIndex];
|
|
14429
14467
|
trackData.startTimestamp = firstBlock.timestamp;
|
|
14430
14468
|
trackData.endTimestamp = lastBlock.timestamp + lastBlock.duration;
|
|
14431
|
-
const track = segment.tracks.find((x) => x.id === trackId);
|
|
14432
14469
|
if (track) {
|
|
14433
14470
|
const insertionIndex2 = binarySearchLessOrEqual(
|
|
14434
14471
|
track.clusters,
|
|
@@ -14470,6 +14507,95 @@ ${cue.notes ?? ""}`;
|
|
|
14470
14507
|
}
|
|
14471
14508
|
return trackData;
|
|
14472
14509
|
}
|
|
14510
|
+
expandLacedBlocks(blocks, track) {
|
|
14511
|
+
for (let blockIndex = 0; blockIndex < blocks.length; blockIndex++) {
|
|
14512
|
+
const originalBlock = blocks[blockIndex];
|
|
14513
|
+
if (originalBlock.lacing === 0 /* None */) {
|
|
14514
|
+
continue;
|
|
14515
|
+
}
|
|
14516
|
+
const data = originalBlock.data;
|
|
14517
|
+
let pos = 0;
|
|
14518
|
+
const frameSizes = [];
|
|
14519
|
+
const frameCount = data[pos] + 1;
|
|
14520
|
+
pos++;
|
|
14521
|
+
switch (originalBlock.lacing) {
|
|
14522
|
+
case 1 /* Xiph */:
|
|
14523
|
+
{
|
|
14524
|
+
let totalUsedSize = 0;
|
|
14525
|
+
for (let i = 0; i < frameCount - 1; i++) {
|
|
14526
|
+
let frameSize = 0;
|
|
14527
|
+
while (pos < data.length) {
|
|
14528
|
+
const value = data[pos];
|
|
14529
|
+
frameSize += value;
|
|
14530
|
+
pos++;
|
|
14531
|
+
if (value < 255) {
|
|
14532
|
+
frameSizes.push(frameSize);
|
|
14533
|
+
totalUsedSize += frameSize;
|
|
14534
|
+
break;
|
|
14535
|
+
}
|
|
14536
|
+
}
|
|
14537
|
+
}
|
|
14538
|
+
frameSizes.push(data.length - (pos + totalUsedSize));
|
|
14539
|
+
}
|
|
14540
|
+
;
|
|
14541
|
+
break;
|
|
14542
|
+
case 2 /* FixedSize */:
|
|
14543
|
+
{
|
|
14544
|
+
const totalDataSize = data.length - 1;
|
|
14545
|
+
const frameSize = Math.floor(totalDataSize / frameCount);
|
|
14546
|
+
for (let i = 0; i < frameCount; i++) {
|
|
14547
|
+
frameSizes.push(frameSize);
|
|
14548
|
+
}
|
|
14549
|
+
}
|
|
14550
|
+
;
|
|
14551
|
+
break;
|
|
14552
|
+
case 3 /* Ebml */:
|
|
14553
|
+
{
|
|
14554
|
+
const firstResult = readVarInt(data, pos);
|
|
14555
|
+
let currentSize = firstResult.value;
|
|
14556
|
+
frameSizes.push(currentSize);
|
|
14557
|
+
pos += firstResult.width;
|
|
14558
|
+
let totalUsedSize = currentSize;
|
|
14559
|
+
for (let i = 1; i < frameCount - 1; i++) {
|
|
14560
|
+
const diffResult = readVarInt(data, pos);
|
|
14561
|
+
const unsignedDiff = diffResult.value;
|
|
14562
|
+
const bias = (1 << diffResult.width * 7 - 1) - 1;
|
|
14563
|
+
const diff = unsignedDiff - bias;
|
|
14564
|
+
currentSize += diff;
|
|
14565
|
+
frameSizes.push(currentSize);
|
|
14566
|
+
pos += diffResult.width;
|
|
14567
|
+
totalUsedSize += currentSize;
|
|
14568
|
+
}
|
|
14569
|
+
frameSizes.push(data.length - (pos + totalUsedSize));
|
|
14570
|
+
}
|
|
14571
|
+
;
|
|
14572
|
+
break;
|
|
14573
|
+
default:
|
|
14574
|
+
assert(false);
|
|
14575
|
+
}
|
|
14576
|
+
assert(frameSizes.length === frameCount);
|
|
14577
|
+
blocks.splice(blockIndex, 1);
|
|
14578
|
+
let dataOffset = pos;
|
|
14579
|
+
for (let i = 0; i < frameCount; i++) {
|
|
14580
|
+
const frameSize = frameSizes[i];
|
|
14581
|
+
const frameData = data.subarray(dataOffset, dataOffset + frameSize);
|
|
14582
|
+
const blockDuration = originalBlock.duration || frameCount * (track?.defaultDuration ?? 0);
|
|
14583
|
+
const frameTimestamp = originalBlock.timestamp + blockDuration * i / frameCount;
|
|
14584
|
+
const frameDuration = blockDuration / frameCount;
|
|
14585
|
+
blocks.splice(blockIndex + i, 0, {
|
|
14586
|
+
timestamp: frameTimestamp,
|
|
14587
|
+
duration: frameDuration,
|
|
14588
|
+
isKeyFrame: originalBlock.isKeyFrame,
|
|
14589
|
+
referencedTimestamps: originalBlock.referencedTimestamps,
|
|
14590
|
+
data: frameData,
|
|
14591
|
+
lacing: 0 /* None */
|
|
14592
|
+
});
|
|
14593
|
+
dataOffset += frameSize;
|
|
14594
|
+
}
|
|
14595
|
+
blockIndex += frameCount;
|
|
14596
|
+
blockIndex--;
|
|
14597
|
+
}
|
|
14598
|
+
}
|
|
14473
14599
|
readContiguousElements(reader, totalSize) {
|
|
14474
14600
|
const startIndex = reader.pos;
|
|
14475
14601
|
while (reader.pos - startIndex <= totalSize - MIN_HEADER_SIZE) {
|
|
@@ -14544,6 +14670,7 @@ ${cue.notes ?? ""}`;
|
|
|
14544
14670
|
inputTrack: null,
|
|
14545
14671
|
codecId: null,
|
|
14546
14672
|
codecPrivate: null,
|
|
14673
|
+
defaultDuration: null,
|
|
14547
14674
|
languageCode: UNDETERMINED_LANGUAGE,
|
|
14548
14675
|
info: null
|
|
14549
14676
|
};
|
|
@@ -14691,6 +14818,13 @@ ${cue.notes ?? ""}`;
|
|
|
14691
14818
|
}
|
|
14692
14819
|
;
|
|
14693
14820
|
break;
|
|
14821
|
+
case 2352003 /* DefaultDuration */:
|
|
14822
|
+
{
|
|
14823
|
+
if (!this.currentTrack) break;
|
|
14824
|
+
this.currentTrack.defaultDuration = this.currentTrack.segment.timestampFactor * reader.readUnsignedInt(size) / 1e9;
|
|
14825
|
+
}
|
|
14826
|
+
;
|
|
14827
|
+
break;
|
|
14694
14828
|
case 2274716 /* Language */:
|
|
14695
14829
|
{
|
|
14696
14830
|
if (!this.currentTrack) break;
|
|
@@ -14869,14 +15003,17 @@ ${cue.notes ?? ""}`;
|
|
|
14869
15003
|
const relativeTimestamp = reader.readS16();
|
|
14870
15004
|
const flags = reader.readU8();
|
|
14871
15005
|
const isKeyFrame = !!(flags & 128);
|
|
15006
|
+
const lacing = flags >> 1 & 3;
|
|
14872
15007
|
const trackData = this.getTrackDataInCluster(this.currentCluster, trackNumber);
|
|
14873
15008
|
trackData.blocks.push({
|
|
14874
15009
|
timestamp: relativeTimestamp,
|
|
14875
15010
|
// We'll add the cluster's timestamp to this later
|
|
14876
15011
|
duration: 0,
|
|
15012
|
+
// Will set later
|
|
14877
15013
|
isKeyFrame,
|
|
14878
15014
|
referencedTimestamps: [],
|
|
14879
|
-
data: reader.readBytes(size - (reader.pos - dataStartPos))
|
|
15015
|
+
data: reader.readBytes(size - (reader.pos - dataStartPos)),
|
|
15016
|
+
lacing
|
|
14880
15017
|
});
|
|
14881
15018
|
}
|
|
14882
15019
|
;
|
|
@@ -14900,14 +15037,17 @@ ${cue.notes ?? ""}`;
|
|
|
14900
15037
|
const trackNumber = reader.readVarInt();
|
|
14901
15038
|
const relativeTimestamp = reader.readS16();
|
|
14902
15039
|
const flags = reader.readU8();
|
|
15040
|
+
const lacing = flags >> 1 & 3;
|
|
14903
15041
|
const trackData = this.getTrackDataInCluster(this.currentCluster, trackNumber);
|
|
14904
15042
|
this.currentBlock = {
|
|
14905
15043
|
timestamp: relativeTimestamp,
|
|
14906
15044
|
// We'll add the cluster's timestamp to this later
|
|
14907
15045
|
duration: 0,
|
|
15046
|
+
// Will set later
|
|
14908
15047
|
isKeyFrame: true,
|
|
14909
15048
|
referencedTimestamps: [],
|
|
14910
|
-
data: reader.readBytes(size - (reader.pos - dataStartPos))
|
|
15049
|
+
data: reader.readBytes(size - (reader.pos - dataStartPos)),
|
|
15050
|
+
lacing
|
|
14911
15051
|
};
|
|
14912
15052
|
trackData.blocks.push(this.currentBlock);
|
|
14913
15053
|
}
|
|
@@ -15010,7 +15150,6 @@ ${cue.notes ?? ""}`;
|
|
|
15010
15150
|
throw new Error("Packet was not created from this track.");
|
|
15011
15151
|
}
|
|
15012
15152
|
const trackData = locationInCluster.cluster.trackData.get(this.internalTrack.id);
|
|
15013
|
-
const block = trackData.blocks[locationInCluster.blockIndex];
|
|
15014
15153
|
const clusterIndex = binarySearchExact(
|
|
15015
15154
|
this.internalTrack.clusters,
|
|
15016
15155
|
locationInCluster.cluster.elementStartPos,
|
|
@@ -15051,7 +15190,8 @@ ${cue.notes ?? ""}`;
|
|
|
15051
15190
|
};
|
|
15052
15191
|
}
|
|
15053
15192
|
},
|
|
15054
|
-
|
|
15193
|
+
-Infinity,
|
|
15194
|
+
// Use -Infinity as a search timestamp to avoid using the cues
|
|
15055
15195
|
Infinity,
|
|
15056
15196
|
options
|
|
15057
15197
|
);
|
|
@@ -15071,7 +15211,6 @@ ${cue.notes ?? ""}`;
|
|
|
15071
15211
|
throw new Error("Packet was not created from this track.");
|
|
15072
15212
|
}
|
|
15073
15213
|
const trackData = locationInCluster.cluster.trackData.get(this.internalTrack.id);
|
|
15074
|
-
const block = trackData.blocks[locationInCluster.blockIndex];
|
|
15075
15214
|
const clusterIndex = binarySearchExact(
|
|
15076
15215
|
this.internalTrack.clusters,
|
|
15077
15216
|
locationInCluster.cluster.elementStartPos,
|
|
@@ -15117,7 +15256,8 @@ ${cue.notes ?? ""}`;
|
|
|
15117
15256
|
};
|
|
15118
15257
|
}
|
|
15119
15258
|
},
|
|
15120
|
-
|
|
15259
|
+
-Infinity,
|
|
15260
|
+
// Use -Infinity as a search timestamp to avoid using the cues
|
|
15121
15261
|
Infinity,
|
|
15122
15262
|
options
|
|
15123
15263
|
);
|
|
@@ -16774,8 +16914,11 @@ ${cue.notes ?? ""}`;
|
|
|
16774
16914
|
if (options.video?.rotate !== void 0 && ![0, 90, 180, 270].includes(options.video.rotate)) {
|
|
16775
16915
|
throw new TypeError("options.video.rotate, when provided, must be 0, 90, 180 or 270.");
|
|
16776
16916
|
}
|
|
16917
|
+
if (options.video?.frameRate !== void 0 && (!Number.isFinite(options.video.frameRate) || options.video.frameRate <= 0)) {
|
|
16918
|
+
throw new TypeError("options.video.frameRate, when provided, must be a finite positive number.");
|
|
16919
|
+
}
|
|
16777
16920
|
if (options.audio !== void 0 && (!options.audio || typeof options.audio !== "object")) {
|
|
16778
|
-
throw new TypeError("options.
|
|
16921
|
+
throw new TypeError("options.audio, when provided, must be an object.");
|
|
16779
16922
|
}
|
|
16780
16923
|
if (options.audio?.discard !== void 0 && typeof options.audio.discard !== "boolean") {
|
|
16781
16924
|
throw new TypeError("options.audio.discard, when provided, must be a boolean.");
|
|
@@ -16942,7 +17085,7 @@ ${cue.notes ?? ""}`;
|
|
|
16942
17085
|
height = ceilToMultipleOfTwo(this._options.video.height);
|
|
16943
17086
|
}
|
|
16944
17087
|
const firstTimestamp = await track.getFirstTimestamp();
|
|
16945
|
-
const needsTranscode = !!this._options.video?.forceTranscode || this._startTimestamp > 0 || firstTimestamp < 0;
|
|
17088
|
+
const needsTranscode = !!this._options.video?.forceTranscode || this._startTimestamp > 0 || firstTimestamp < 0 || !!this._options.video?.frameRate;
|
|
16946
17089
|
const needsRerender = width !== originalWidth || height !== originalHeight || totalRotation !== 0 && !outputSupportsRotation;
|
|
16947
17090
|
let videoCodecs = this.output.format.getSupportedVideoCodecs();
|
|
16948
17091
|
if (!needsTranscode && !this._options.video?.bitrate && !needsRerender && videoCodecs.includes(sourceCodec) && (!this._options.video?.codec || this._options.video?.codec === sourceCodec)) {
|
|
@@ -16993,9 +17136,9 @@ ${cue.notes ?? ""}`;
|
|
|
16993
17136
|
bitrate,
|
|
16994
17137
|
onEncodedPacket: (sample) => this._reportProgress(track.id, sample.timestamp + sample.duration)
|
|
16995
17138
|
};
|
|
17139
|
+
const source = new VideoSampleSource(encodingConfig);
|
|
17140
|
+
videoSource = source;
|
|
16996
17141
|
if (needsRerender) {
|
|
16997
|
-
const source = new VideoSampleSource(encodingConfig);
|
|
16998
|
-
videoSource = source;
|
|
16999
17142
|
this._trackPromises.push((async () => {
|
|
17000
17143
|
await this._started;
|
|
17001
17144
|
const sink = new CanvasSink(track, {
|
|
@@ -17007,6 +17150,22 @@ ${cue.notes ?? ""}`;
|
|
|
17007
17150
|
poolSize: 1
|
|
17008
17151
|
});
|
|
17009
17152
|
const iterator = sink.canvases(this._startTimestamp, this._endTimestamp);
|
|
17153
|
+
const frameRate = this._options.video?.frameRate;
|
|
17154
|
+
let lastCanvas = null;
|
|
17155
|
+
let lastCanvasTimestamp = null;
|
|
17156
|
+
let lastCanvasEndTimestamp = null;
|
|
17157
|
+
const padFrames = async (until) => {
|
|
17158
|
+
assert(lastCanvas);
|
|
17159
|
+
assert(frameRate !== void 0);
|
|
17160
|
+
const frameDifference = Math.round((until - lastCanvasTimestamp) * frameRate);
|
|
17161
|
+
for (let i = 1; i < frameDifference; i++) {
|
|
17162
|
+
const sample = new VideoSample(lastCanvas, {
|
|
17163
|
+
timestamp: lastCanvasTimestamp + i / frameRate,
|
|
17164
|
+
duration: 1 / frameRate
|
|
17165
|
+
});
|
|
17166
|
+
await source.add(sample);
|
|
17167
|
+
}
|
|
17168
|
+
};
|
|
17010
17169
|
for await (const { canvas, timestamp, duration } of iterator) {
|
|
17011
17170
|
if (this._synchronizer.shouldWait(track.id, timestamp)) {
|
|
17012
17171
|
await this._synchronizer.wait(timestamp);
|
|
@@ -17014,30 +17173,98 @@ ${cue.notes ?? ""}`;
|
|
|
17014
17173
|
if (this._canceled) {
|
|
17015
17174
|
return;
|
|
17016
17175
|
}
|
|
17176
|
+
let adjustedSampleTimestamp = Math.max(timestamp - this._startTimestamp, 0);
|
|
17177
|
+
lastCanvasEndTimestamp = timestamp + duration;
|
|
17178
|
+
if (frameRate !== void 0) {
|
|
17179
|
+
const alignedTimestamp = Math.floor(adjustedSampleTimestamp * frameRate) / frameRate;
|
|
17180
|
+
if (lastCanvas !== null) {
|
|
17181
|
+
if (alignedTimestamp <= lastCanvasTimestamp) {
|
|
17182
|
+
lastCanvas = canvas;
|
|
17183
|
+
lastCanvasTimestamp = alignedTimestamp;
|
|
17184
|
+
continue;
|
|
17185
|
+
} else {
|
|
17186
|
+
await padFrames(alignedTimestamp);
|
|
17187
|
+
}
|
|
17188
|
+
}
|
|
17189
|
+
adjustedSampleTimestamp = alignedTimestamp;
|
|
17190
|
+
}
|
|
17017
17191
|
const sample = new VideoSample(canvas, {
|
|
17018
|
-
timestamp:
|
|
17019
|
-
duration
|
|
17192
|
+
timestamp: adjustedSampleTimestamp,
|
|
17193
|
+
duration: frameRate !== void 0 ? 1 / frameRate : duration
|
|
17020
17194
|
});
|
|
17021
17195
|
await source.add(sample);
|
|
17022
|
-
|
|
17196
|
+
if (frameRate !== void 0) {
|
|
17197
|
+
lastCanvas = canvas;
|
|
17198
|
+
lastCanvasTimestamp = adjustedSampleTimestamp;
|
|
17199
|
+
} else {
|
|
17200
|
+
sample.close();
|
|
17201
|
+
}
|
|
17202
|
+
}
|
|
17203
|
+
if (lastCanvas) {
|
|
17204
|
+
assert(lastCanvasEndTimestamp !== null);
|
|
17205
|
+
assert(frameRate !== void 0);
|
|
17206
|
+
await padFrames(Math.floor(lastCanvasEndTimestamp * frameRate) / frameRate);
|
|
17023
17207
|
}
|
|
17208
|
+
source.close();
|
|
17209
|
+
this._synchronizer.closeTrack(track.id);
|
|
17024
17210
|
})());
|
|
17025
17211
|
} else {
|
|
17026
|
-
const source = new VideoSampleSource(encodingConfig);
|
|
17027
|
-
videoSource = source;
|
|
17028
17212
|
this._trackPromises.push((async () => {
|
|
17029
17213
|
await this._started;
|
|
17030
17214
|
const sink = new VideoSampleSink(track);
|
|
17215
|
+
const frameRate = this._options.video?.frameRate;
|
|
17216
|
+
let lastSample = null;
|
|
17217
|
+
let lastSampleTimestamp = null;
|
|
17218
|
+
let lastSampleEndTimestamp = null;
|
|
17219
|
+
const padFrames = async (until) => {
|
|
17220
|
+
assert(lastSample);
|
|
17221
|
+
assert(frameRate !== void 0);
|
|
17222
|
+
const frameDifference = Math.round((until - lastSampleTimestamp) * frameRate);
|
|
17223
|
+
for (let i = 1; i < frameDifference; i++) {
|
|
17224
|
+
lastSample.setTimestamp(lastSampleTimestamp + i / frameRate);
|
|
17225
|
+
lastSample.setDuration(1 / frameRate);
|
|
17226
|
+
await source.add(lastSample);
|
|
17227
|
+
}
|
|
17228
|
+
lastSample.close();
|
|
17229
|
+
};
|
|
17031
17230
|
for await (const sample of sink.samples(this._startTimestamp, this._endTimestamp)) {
|
|
17032
17231
|
if (this._synchronizer.shouldWait(track.id, sample.timestamp)) {
|
|
17033
17232
|
await this._synchronizer.wait(sample.timestamp);
|
|
17034
17233
|
}
|
|
17035
|
-
sample.setTimestamp(Math.max(sample.timestamp - this._startTimestamp, 0));
|
|
17036
17234
|
if (this._canceled) {
|
|
17235
|
+
lastSample?.close();
|
|
17037
17236
|
return;
|
|
17038
17237
|
}
|
|
17238
|
+
let adjustedSampleTimestamp = Math.max(sample.timestamp - this._startTimestamp, 0);
|
|
17239
|
+
lastSampleEndTimestamp = sample.timestamp + sample.duration;
|
|
17240
|
+
if (frameRate !== void 0) {
|
|
17241
|
+
const alignedTimestamp = Math.floor(adjustedSampleTimestamp * frameRate) / frameRate;
|
|
17242
|
+
if (lastSample !== null) {
|
|
17243
|
+
if (alignedTimestamp <= lastSampleTimestamp) {
|
|
17244
|
+
lastSample.close();
|
|
17245
|
+
lastSample = sample;
|
|
17246
|
+
lastSampleTimestamp = alignedTimestamp;
|
|
17247
|
+
continue;
|
|
17248
|
+
} else {
|
|
17249
|
+
await padFrames(alignedTimestamp);
|
|
17250
|
+
}
|
|
17251
|
+
}
|
|
17252
|
+
adjustedSampleTimestamp = alignedTimestamp;
|
|
17253
|
+
sample.setDuration(1 / frameRate);
|
|
17254
|
+
}
|
|
17255
|
+
sample.setTimestamp(adjustedSampleTimestamp);
|
|
17039
17256
|
await source.add(sample);
|
|
17040
|
-
|
|
17257
|
+
if (frameRate !== void 0) {
|
|
17258
|
+
lastSample = sample;
|
|
17259
|
+
lastSampleTimestamp = adjustedSampleTimestamp;
|
|
17260
|
+
} else {
|
|
17261
|
+
sample.close();
|
|
17262
|
+
}
|
|
17263
|
+
}
|
|
17264
|
+
if (lastSample) {
|
|
17265
|
+
assert(lastSampleEndTimestamp !== null);
|
|
17266
|
+
assert(frameRate !== void 0);
|
|
17267
|
+
await padFrames(Math.floor(lastSampleEndTimestamp * frameRate) / frameRate);
|
|
17041
17268
|
}
|
|
17042
17269
|
source.close();
|
|
17043
17270
|
this._synchronizer.closeTrack(track.id);
|
|
@@ -17045,6 +17272,7 @@ ${cue.notes ?? ""}`;
|
|
|
17045
17272
|
}
|
|
17046
17273
|
}
|
|
17047
17274
|
this.output.addVideoTrack(videoSource, {
|
|
17275
|
+
frameRate: this._options.video?.frameRate,
|
|
17048
17276
|
languageCode: track.languageCode,
|
|
17049
17277
|
rotation: needsRerender ? 0 : totalRotation
|
|
17050
17278
|
// Rerendering will bake the rotation into the output
|