diff --git a/dev/demux.html b/dev/demux.html
index 1ee7517..fb721a9 100644
--- a/dev/demux.html
+++ b/dev/demux.html
@@ -49,6 +49,10 @@
const videoTrack = await input.getPrimaryVideoTrack();
const drain = new Metamuxer.EncodedVideoSampleDrain(videoTrack);
+ console.log(await videoTrack.computeSampleStats())
+ document.body.textContent = performance.now() - start;
+ return;
+
const decoderConfig = await videoTrack.getDecoderConfig();
const sampleSource = new Metamuxer.EncodedVideoSampleSource(await videoTrack.getCodec());
output.addVideoTrack(sampleSource);
@@ -65,7 +69,7 @@
}
await output.finalize();
- document.body.textContent = performance.now() - start;
+
console.log(target)
diff --git a/src/index.ts b/src/index.ts
index c58cbed..5d6ef4e 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -58,7 +58,7 @@ export {
WEBM,
} from './input-format';
export { Input, InputOptions } from './input';
-export { InputTrack, InputVideoTrack, InputAudioTrack } from './input-track';
+export { InputTrack, InputVideoTrack, InputAudioTrack, SampleStats } from './input-track';
export {
EncodedVideoSample,
EncodedAudioSample,
diff --git a/src/input-track.ts b/src/input-track.ts
index 198e02c..8522ebb 100644
--- a/src/input-track.ts
+++ b/src/input-track.ts
@@ -1,10 +1,11 @@
import { AudioCodec, MediaCodec, VideoCodec } from './codec';
-import { SampleRetrievalOptions } from './media-drain';
+import { EncodedAudioSampleDrain, EncodedVideoSampleDrain, SampleRetrievalOptions } from './media-drain';
import { Rotation } from './misc';
import { EncodedAudioSample, EncodedVideoSample } from './sample';
export interface InputTrackBacking {
getCodec(): Promise;
+ getFirstTimestamp(): Promise;
computeDuration(): Promise;
}
@@ -30,6 +31,10 @@ export abstract class InputTrack {
return this instanceof InputAudioTrack;
}
+ getFirstTimestamp() {
+ return this._backing.getFirstTimestamp();
+ }
+
computeDuration() {
return this._backing.computeDuration();
}
@@ -109,6 +114,10 @@ export class InputVideoTrack extends InputTrack {
return false;
}
}
+
+ computeSampleStats() {
+ return computeSampleStats(new EncodedVideoSampleDrain(this));
+ }
}
export interface InputAudioTrackBacking extends InputTrackBacking {
@@ -174,4 +183,36 @@ export class InputAudioTrack extends InputTrack {
return false;
}
}
+
+ computeSampleStats() {
+ return computeSampleStats(new EncodedAudioSampleDrain(this));
+ }
}
+
+/** @public */
+export type SampleStats = {
+ sampleCount: number;
+ averageSampleRate: number;
+ averageBitrate: number;
+};
+
+const computeSampleStats = async (drain: EncodedVideoSampleDrain | EncodedAudioSampleDrain): Promise => {
+ let startTimestamp = Infinity;
+ let endTimestamp = -Infinity;
+ let sampleCount = 0;
+ let totalSampleBytes = 0;
+
+ for await (const sample of drain.samples(undefined, undefined, { metadataOnly: true })) {
+ startTimestamp = Math.min(startTimestamp, sample.timestamp);
+ endTimestamp = Math.max(endTimestamp, sample.timestamp + sample.duration);
+
+ sampleCount++;
+ totalSampleBytes += sample.byteLength;
+ }
+
+ return {
+ sampleCount,
+ averageSampleRate: sampleCount ? Math.fround(sampleCount / (endTimestamp - startTimestamp)) : 0,
+ averageBitrate: sampleCount ? Math.fround(8 * totalSampleBytes / (endTimestamp - startTimestamp)) : 0,
+ };
+};
diff --git a/src/isobmff/isobmff-demuxer.ts b/src/isobmff/isobmff-demuxer.ts
index d5a61d3..4a2c90c 100644
--- a/src/isobmff/isobmff-demuxer.ts
+++ b/src/isobmff/isobmff-demuxer.ts
@@ -1673,6 +1673,11 @@ abstract class IsobmffTrackBacking<
throw new Error('Not implemented on base class.');
}
+ async getFirstTimestamp() {
+ const firstSample = await this.getFirstSample({ metadataOnly: true });
+ return firstSample?.timestamp ?? 0;
+ }
+
async computeDuration() {
const lastSample = await this.getSample(Infinity, { metadataOnly: true });
return (lastSample?.timestamp ?? 0) + (lastSample?.duration ?? 0);
diff --git a/todo.txt b/todo.txt
index 30470b0..cc92b54 100644
--- a/todo.txt
+++ b/todo.txt
@@ -2,8 +2,8 @@
- Throw if edit list detected
- Add the new audio codecs to muxers
- Mov muxer!!! Only mov can hold PCM audio, MP4 cannot
-- Metadata methods for computing average fps and bitrate
- A stream source?? Or like a callback-driven source
- onRead callback for Source
- onHeader, etc callbacks for Matroska
-- Add cslg box
\ No newline at end of file
+- Add cslg box
+- https://github.com/Vanilagy/mp4-muxer/issues/83 tell him it's possible now
\ No newline at end of file