diff --git a/dev/demux.html b/dev/demux.html index 5138016..29f4027 100644 --- a/dev/demux.html +++ b/dev/demux.html @@ -20,6 +20,7 @@ console.log(await videoTrack.getFirstTimestamp(), await videoTrack.computeDuration()); console.log(videoTrack.name); + console.log(videoTrack.internalCodecId); /* const sink = new Mediabunny.EncodedPacketSink(videoTrack); diff --git a/docs/guide/reading-media-files.md b/docs/guide/reading-media-files.md index 996b647..8e24aaf 100644 --- a/docs/guide/reading-media-files.md +++ b/docs/guide/reading-media-files.md @@ -108,7 +108,7 @@ You can query metadata related to the track's codec: ```ts track.codec; // => MediaCodec | null ``` -This field is `null` when the track's codec couldn't be recognized or is not supported by Mediabunny. See [Codecs](./supported-formats-and-codecs#codecs) for the full list of supported codecs. +This field is `null` when the track's codec couldn't be recognized or is not supported by Mediabunny. See [Codecs](./supported-formats-and-codecs#codecs) for the full list of supported codecs. When Mediabunny doesn't recognize the format, you can still use the `internalCodecId` field to figure out the codec of the track, although its format depends on the container format used and is not homogenized by Mediabunny. You can also extract the full codec parameter string from the track, as specified in the [WebCodecs Codec Registry](https://www.w3.org/TR/webcodecs-codec-registry/): ```ts diff --git a/src/adts/adts-demuxer.ts b/src/adts/adts-demuxer.ts index 05deace..e6d6f32 100644 --- a/src/adts/adts-demuxer.ts +++ b/src/adts/adts-demuxer.ts @@ -168,6 +168,12 @@ class AdtsAudioTrackBacking implements InputAudioTrackBacking { return 'aac'; } + getInternalCodecId() { + assert(this.demuxer.firstFrameHeader); + + return this.demuxer.firstFrameHeader.objectType; + } + getNumberOfChannels() { assert(this.demuxer.firstFrameHeader); diff --git a/src/input-track.ts b/src/input-track.ts index a433462..bb5ec42 100644 --- a/src/input-track.ts +++ b/src/input-track.ts @@ -30,6 +30,7 @@ export type PacketStats = { export interface InputTrackBacking { getId(): number; getCodec(): MediaCodec | null; + getInternalCodecId(): string | number | Uint8Array | null; getName(): string | null; getLanguageCode(): string; getTimeResolution(): number; @@ -85,6 +86,22 @@ export abstract class InputTrack { return this._backing.getId(); } + /** + * The identifier of the codec used internally by the container. It is not homogenized by Mediabunny + * and depends entirely on the container format. + * + * This field can be used to determine the codec of a track in case Mediabunny doesn't know that codec. + * + * - For ISOBMFF files, this field returns the name of the Sample Description Box (e.g. 'avc1'). + * - For Matroska files, this field returns the value of the CodecID element. + * - For WAVE files, this field returns the value of the format tag in the 'fmt ' chunk. + * - For ADTS files, this field contains the MPEG-4 Audio Object Type. + * - In all other cases, this field is `null`. + */ + get internalCodecId() { + return this._backing.getInternalCodecId(); + } + /** The ISO 639-2/T language code for this track. If the language is unknown, this field is 'und' (undetermined). */ get languageCode() { return this._backing.getLanguageCode(); diff --git a/src/isobmff/isobmff-demuxer.ts b/src/isobmff/isobmff-demuxer.ts index 1920049..20723ff 100644 --- a/src/isobmff/isobmff-demuxer.ts +++ b/src/isobmff/isobmff-demuxer.ts @@ -71,6 +71,7 @@ type InternalTrack = { durationInMovieTimescale: number; durationInMediaTimescale: number; rotation: Rotation; + internalCodecId: string | null; name: string | null; languageCode: string; sampleTableByteOffset: number; @@ -628,6 +629,7 @@ export class IsobmffDemuxer extends Demuxer { durationInMovieTimescale: -1, durationInMediaTimescale: -1, rotation: 0, + internalCodecId: null, name: null, languageCode: UNDETERMINED_LANGUAGE, sampleTableByteOffset: -1, @@ -847,6 +849,7 @@ export class IsobmffDemuxer extends Demuxer { break; } + track.internalCodecId = sampleBoxInfo.name; const lowercaseBoxName = sampleBoxInfo.name.toLowerCase(); if (track.info.type === 'video') { @@ -1957,6 +1960,10 @@ abstract class IsobmffTrackBacking implements InputTrackBacking { throw new Error('Not implemented on base class.'); } + getInternalCodecId() { + return this.internalTrack.internalCodecId; + } + getName() { return this.internalTrack.name; } diff --git a/src/matroska/matroska-demuxer.ts b/src/matroska/matroska-demuxer.ts index 86dff1d..a1f4b37 100644 --- a/src/matroska/matroska-demuxer.ts +++ b/src/matroska/matroska-demuxer.ts @@ -1282,6 +1282,10 @@ abstract class MatroskaTrackBacking implements InputTrackBacking { throw new Error('Not implemented on base class.'); } + getInternalCodecId() { + return this.internalTrack.codecId; + } + async computeDuration() { const lastPacket = await this.getPacket(Infinity, { metadataOnly: true }); return (lastPacket?.timestamp ?? 0) + (lastPacket?.duration ?? 0); diff --git a/src/mp3/mp3-demuxer.ts b/src/mp3/mp3-demuxer.ts index 315d431..012b91c 100644 --- a/src/mp3/mp3-demuxer.ts +++ b/src/mp3/mp3-demuxer.ts @@ -180,6 +180,10 @@ class Mp3AudioTrackBacking implements InputAudioTrackBacking { return 'mp3'; } + getInternalCodecId() { + return null; + } + getNumberOfChannels() { assert(this.demuxer.firstFrameHeader); return this.demuxer.firstFrameHeader.channel === 3 ? 1 : 2; diff --git a/src/ogg/ogg-demuxer.ts b/src/ogg/ogg-demuxer.ts index f0a6b5c..39a5ed3 100644 --- a/src/ogg/ogg-demuxer.ts +++ b/src/ogg/ogg-demuxer.ts @@ -431,6 +431,10 @@ class OggAudioTrackBacking implements InputAudioTrackBacking { return this.bitstream.codecInfo.codec; } + getInternalCodecId() { + return null; + } + async getDecoderConfig(): Promise { assert(this.bitstream.codecInfo.codec); diff --git a/src/wave/wave-demuxer.ts b/src/wave/wave-demuxer.ts index df7a7f3..1c3aa7d 100644 --- a/src/wave/wave-demuxer.ts +++ b/src/wave/wave-demuxer.ts @@ -222,6 +222,11 @@ class WaveAudioTrackBacking implements InputAudioTrackBacking { return this.demuxer.getCodec(); } + getInternalCodecId() { + assert(this.demuxer.audioInfo); + return this.demuxer.audioInfo.format; + } + async getDecoderConfig(): Promise { const codec = this.demuxer.getCodec(); if (!codec) {