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