mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-10-02 21:33:52 +02:00
Add track name support
This commit is contained in:
@@ -19,6 +19,8 @@
|
|||||||
const videoTrack = await input.getPrimaryVideoTrack();
|
const videoTrack = await input.getPrimaryVideoTrack();
|
||||||
console.log(await videoTrack.getFirstTimestamp(), await videoTrack.computeDuration());
|
console.log(await videoTrack.getFirstTimestamp(), await videoTrack.computeDuration());
|
||||||
|
|
||||||
|
console.log(videoTrack.name);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
const sink = new Mediabunny.EncodedPacketSink(videoTrack);
|
const sink = new Mediabunny.EncodedPacketSink(videoTrack);
|
||||||
|
|
||||||
|
|||||||
+4
-3
@@ -45,6 +45,7 @@
|
|||||||
format = new Mediabunny.MkvOutputFormat({ minimumClusterDuration: 2 });
|
format = new Mediabunny.MkvOutputFormat({ minimumClusterDuration: 2 });
|
||||||
format = new Mediabunny.WavOutputFormat();
|
format = new Mediabunny.WavOutputFormat();
|
||||||
format = new Mediabunny.MkvOutputFormat();
|
format = new Mediabunny.MkvOutputFormat();
|
||||||
|
format = new Mediabunny.MovOutputFormat();
|
||||||
let target = new Mediabunny.BufferTarget();
|
let target = new Mediabunny.BufferTarget();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -125,8 +126,8 @@
|
|||||||
});
|
});
|
||||||
let subtitleSource = new Mediabunny.TextSubtitleSource('webvtt');
|
let subtitleSource = new Mediabunny.TextSubtitleSource('webvtt');
|
||||||
|
|
||||||
output.addVideoTrack(videoSource, { languageCode: 'eng' });
|
output.addVideoTrack(videoSource, { languageCode: 'eng', name: 'Mononoké' });
|
||||||
output.addAudioTrack(audioSource);
|
output.addAudioTrack(audioSource, { name: 'Yooo' });
|
||||||
//output.addSubtitleTrack(subtitleSource);
|
//output.addSubtitleTrack(subtitleSource);
|
||||||
|
|
||||||
output.start();
|
output.start();
|
||||||
@@ -206,5 +207,5 @@ Testing... <00:17.350>One... <00:18.125>Two...
|
|||||||
await output.finalize();
|
await output.finalize();
|
||||||
|
|
||||||
console.log(target);
|
console.log(target);
|
||||||
//download(new Blob([target.buffer]), 'test' + format.fileExtension);
|
download(new Blob([target.buffer]), 'test' + format.fileExtension);
|
||||||
</script>
|
</script>
|
||||||
@@ -97,6 +97,9 @@ track.isAudioTrack(); // => boolean
|
|||||||
// Retrieve the track's language as an ISO 639-2/T language code.
|
// Retrieve the track's language as an ISO 639-2/T language code.
|
||||||
// Resolves to 'und' (undetermined) if the language isn't known.
|
// Resolves to 'und' (undetermined) if the language isn't known.
|
||||||
track.languageCode; // => string
|
track.languageCode; // => string
|
||||||
|
|
||||||
|
// A user-defined name for this track.
|
||||||
|
track.name; // => string
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Codec information
|
#### Codec information
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ output.addVideoTrack(videoSource, {
|
|||||||
// This adds two audio tracks; one in English and one in German.
|
// This adds two audio tracks; one in English and one in German.
|
||||||
output.addAudioTrack(audioSourceEng, {
|
output.addAudioTrack(audioSourceEng, {
|
||||||
language: 'eng', // ISO 639-2/T language code
|
language: 'eng', // ISO 639-2/T language code
|
||||||
|
name: 'Developer Commentary', // Sets a user-defined track name
|
||||||
});
|
});
|
||||||
output.addAudioTrack(audioSourceGer, {
|
output.addAudioTrack(audioSourceGer, {
|
||||||
language: 'ger',
|
language: 'ger',
|
||||||
|
|||||||
@@ -156,6 +156,10 @@ class AdtsAudioTrackBacking implements InputAudioTrackBacking {
|
|||||||
return (lastPacket?.timestamp ?? 0) + (lastPacket?.duration ?? 0);
|
return (lastPacket?.timestamp ?? 0) + (lastPacket?.duration ?? 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
getLanguageCode() {
|
getLanguageCode() {
|
||||||
return UNDETERMINED_LANGUAGE;
|
return UNDETERMINED_LANGUAGE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export type PacketStats = {
|
|||||||
export interface InputTrackBacking {
|
export interface InputTrackBacking {
|
||||||
getId(): number;
|
getId(): number;
|
||||||
getCodec(): MediaCodec | null;
|
getCodec(): MediaCodec | null;
|
||||||
|
getName(): string | null;
|
||||||
getLanguageCode(): string;
|
getLanguageCode(): string;
|
||||||
getTimeResolution(): number;
|
getTimeResolution(): number;
|
||||||
getFirstTimestamp(): Promise<number>;
|
getFirstTimestamp(): Promise<number>;
|
||||||
@@ -89,6 +90,11 @@ export abstract class InputTrack {
|
|||||||
return this._backing.getLanguageCode();
|
return this._backing.getLanguageCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A user-defined name for this track. */
|
||||||
|
get name() {
|
||||||
|
return this._backing.getName();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A positive number x such that all timestamps and durations of all packets of this track are
|
* A positive number x such that all timestamps and durations of all packets of this track are
|
||||||
* integer multiples of 1/x.
|
* integer multiples of 1/x.
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import {
|
|||||||
import { formatSubtitleTimestamp } from '../subtitles';
|
import { formatSubtitleTimestamp } from '../subtitles';
|
||||||
import { Writer } from '../writer';
|
import { Writer } from '../writer';
|
||||||
import {
|
import {
|
||||||
|
getTrackMetadata,
|
||||||
GLOBAL_TIMESCALE,
|
GLOBAL_TIMESCALE,
|
||||||
intoTimescale,
|
intoTimescale,
|
||||||
IsobmffAudioTrackData,
|
IsobmffAudioTrackData,
|
||||||
@@ -378,10 +379,72 @@ export const mvhd = (
|
|||||||
* independent of the other tracks in the movie and carries its own temporal and spatial information. Each Track Box
|
* independent of the other tracks in the movie and carries its own temporal and spatial information. Each Track Box
|
||||||
* contains its associated Media Box.
|
* contains its associated Media Box.
|
||||||
*/
|
*/
|
||||||
export const trak = (trackData: IsobmffTrackData, creationTime: number) => box('trak', undefined, [
|
export const trak = (trackData: IsobmffTrackData, creationTime: number) => {
|
||||||
tkhd(trackData, creationTime),
|
const trackMetadata = getTrackMetadata(trackData);
|
||||||
mdia(trackData, creationTime),
|
|
||||||
]);
|
return box('trak', undefined, [
|
||||||
|
tkhd(trackData, creationTime),
|
||||||
|
mdia(trackData, creationTime),
|
||||||
|
trackMetadata.name !== undefined
|
||||||
|
? box('udta', undefined, [
|
||||||
|
box('©nam', [
|
||||||
|
...textEncoder.encode(trackMetadata.name),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
: null,
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
const meta = (trackData: IsobmffTrackData) => {
|
||||||
|
const trackMetadata = getTrackMetadata(trackData);
|
||||||
|
|
||||||
|
if (trackData.muxer.isQuickTime) {
|
||||||
|
const keyMap: Record<keyof IsobmffMetadata, string> = {
|
||||||
|
name: 'com.apple.quicktime.title',
|
||||||
|
};
|
||||||
|
|
||||||
|
return box('meta', undefined, [
|
||||||
|
hdlr(false, 'mdta', ''),
|
||||||
|
fullBox('keys', 0, 0, [
|
||||||
|
u32(Object.keys(trackMetadata).length),
|
||||||
|
], Object.keys(trackMetadata).map(key =>
|
||||||
|
box('mdta', [
|
||||||
|
ascii(keyMap[key as keyof IsobmffMetadata]), // Key name
|
||||||
|
]),
|
||||||
|
)),
|
||||||
|
box('ilst', undefined, Object.values(trackMetadata)
|
||||||
|
.map((value, i) => box(u32(i + 1).map(x => String.fromCharCode(x)).join(''), undefined, [
|
||||||
|
data(value),
|
||||||
|
]))),
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
const keyMap: Record<keyof IsobmffMetadata, string> = {
|
||||||
|
name: '©nam',
|
||||||
|
};
|
||||||
|
|
||||||
|
return fullBox('meta', 0, 0, undefined, [
|
||||||
|
hdlr(false, 'mdir', ''),
|
||||||
|
box('ilst', undefined, Object.entries(trackMetadata)
|
||||||
|
.map(([key, value]) => box(keyMap[key as keyof IsobmffMetadata], undefined, [
|
||||||
|
data(value),
|
||||||
|
]))),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const data = (value: unknown) => {
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
return box('data', [
|
||||||
|
u32(1), // Type indicator (UTF-8)
|
||||||
|
u32(0), // Locale indicator
|
||||||
|
...textEncoder.encode(value),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Unhandled data type.');
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
/** Track Header Box: Specifies the characteristics of a single track within a movie. */
|
/** Track Header Box: Specifies the characteristics of a single track within a movie. */
|
||||||
export const tkhd = (
|
export const tkhd = (
|
||||||
@@ -425,7 +488,7 @@ export const tkhd = (
|
|||||||
/** Media Box: Describes and define a track's media type and sample data. */
|
/** Media Box: Describes and define a track's media type and sample data. */
|
||||||
export const mdia = (trackData: IsobmffTrackData, creationTime: number) => box('mdia', undefined, [
|
export const mdia = (trackData: IsobmffTrackData, creationTime: number) => box('mdia', undefined, [
|
||||||
mdhd(trackData, creationTime),
|
mdhd(trackData, creationTime),
|
||||||
hdlr(trackData),
|
hdlr(true, TRACK_TYPE_TO_COMPONENT_SUBTYPE[trackData.type], TRACK_TYPE_TO_HANDLER_NAME[trackData.type]),
|
||||||
minf(trackData),
|
minf(trackData),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -471,14 +534,14 @@ const TRACK_TYPE_TO_HANDLER_NAME: Record<IsobmffTrackData['type'], string> = {
|
|||||||
subtitle: 'MediabunnyTextHandler',
|
subtitle: 'MediabunnyTextHandler',
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Handler Reference Box: Specifies the media handler component that is to be used to interpret the media's data. */
|
/** Handler Reference Box. */
|
||||||
export const hdlr = (trackData: IsobmffTrackData) => fullBox('hdlr', 0, 0, [
|
export const hdlr = (hasComponentType: boolean, handlerType: string, name: string) => fullBox('hdlr', 0, 0, [
|
||||||
ascii('mhlr'), // Component type
|
hasComponentType ? ascii('mhlr') : u32(0), // Component type
|
||||||
ascii(TRACK_TYPE_TO_COMPONENT_SUBTYPE[trackData.type]), // Component subtype
|
ascii(handlerType), // Component subtype
|
||||||
u32(0), // Component manufacturer
|
u32(0), // Component manufacturer
|
||||||
u32(0), // Component flags
|
u32(0), // Component flags
|
||||||
u32(0), // Component flags mask
|
u32(0), // Component flags mask
|
||||||
ascii(TRACK_TYPE_TO_HANDLER_NAME[trackData.type], true), // Component name
|
ascii(name, true), // Component name
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ import {
|
|||||||
normalizeRotation,
|
normalizeRotation,
|
||||||
Bitstream,
|
Bitstream,
|
||||||
insertSorted,
|
insertSorted,
|
||||||
|
textDecoder,
|
||||||
} from '../misc';
|
} from '../misc';
|
||||||
import { EncodedPacket, PLACEHOLDER_DATA } from '../packet';
|
import { EncodedPacket, PLACEHOLDER_DATA } from '../packet';
|
||||||
import { Reader } from '../reader';
|
import { Reader } from '../reader';
|
||||||
@@ -70,6 +71,7 @@ type InternalTrack = {
|
|||||||
durationInMovieTimescale: number;
|
durationInMovieTimescale: number;
|
||||||
durationInMediaTimescale: number;
|
durationInMediaTimescale: number;
|
||||||
rotation: Rotation;
|
rotation: Rotation;
|
||||||
|
name: string | null;
|
||||||
languageCode: string;
|
languageCode: string;
|
||||||
sampleTableByteOffset: number;
|
sampleTableByteOffset: number;
|
||||||
sampleTable: SampleTable | null;
|
sampleTable: SampleTable | null;
|
||||||
@@ -596,7 +598,8 @@ export class IsobmffDemuxer extends Demuxer {
|
|||||||
case 'minf':
|
case 'minf':
|
||||||
case 'dinf':
|
case 'dinf':
|
||||||
case 'mfra':
|
case 'mfra':
|
||||||
case 'edts': {
|
case 'edts':
|
||||||
|
case 'udta': {
|
||||||
this.readContiguousBoxes(boxInfo.contentSize);
|
this.readContiguousBoxes(boxInfo.contentSize);
|
||||||
}; break;
|
}; break;
|
||||||
|
|
||||||
@@ -625,6 +628,7 @@ export class IsobmffDemuxer extends Demuxer {
|
|||||||
durationInMovieTimescale: -1,
|
durationInMovieTimescale: -1,
|
||||||
durationInMediaTimescale: -1,
|
durationInMediaTimescale: -1,
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
|
name: null,
|
||||||
languageCode: UNDETERMINED_LANGUAGE,
|
languageCode: UNDETERMINED_LANGUAGE,
|
||||||
sampleTableByteOffset: -1,
|
sampleTableByteOffset: -1,
|
||||||
sampleTable: null,
|
sampleTable: null,
|
||||||
@@ -1919,6 +1923,16 @@ export class IsobmffDemuxer extends Demuxer {
|
|||||||
|
|
||||||
this.currentFragment.implicitBaseDataOffset = currentOffset;
|
this.currentFragment.implicitBaseDataOffset = currentOffset;
|
||||||
}; break;
|
}; break;
|
||||||
|
|
||||||
|
// These appear in udta:
|
||||||
|
case '©nam':
|
||||||
|
case 'name': {
|
||||||
|
if (!this.currentTrack) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentTrack.name = textDecoder.decode(this.metadataReader.readBytes(boxInfo.contentSize));
|
||||||
|
}; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.metadataReader.pos = boxEndPos;
|
this.metadataReader.pos = boxEndPos;
|
||||||
@@ -1943,6 +1957,10 @@ abstract class IsobmffTrackBacking implements InputTrackBacking {
|
|||||||
throw new Error('Not implemented on base class.');
|
throw new Error('Not implemented on base class.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
return this.internalTrack.name;
|
||||||
|
}
|
||||||
|
|
||||||
getLanguageCode() {
|
getLanguageCode() {
|
||||||
return this.internalTrack.languageCode;
|
return this.internalTrack.languageCode;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,6 +117,21 @@ export type IsobmffVideoTrackData = IsobmffTrackData & { type: 'video' };
|
|||||||
export type IsobmffAudioTrackData = IsobmffTrackData & { type: 'audio' };
|
export type IsobmffAudioTrackData = IsobmffTrackData & { type: 'audio' };
|
||||||
export type IsobmffSubtitleTrackData = IsobmffTrackData & { type: 'subtitle' };
|
export type IsobmffSubtitleTrackData = IsobmffTrackData & { type: 'subtitle' };
|
||||||
|
|
||||||
|
export type IsobmffMetadata = {
|
||||||
|
name?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getTrackMetadata = (trackData: IsobmffTrackData) => {
|
||||||
|
const metadata: IsobmffMetadata = {};
|
||||||
|
const track = trackData.track as OutputTrack;
|
||||||
|
|
||||||
|
if (track.metadata.name !== undefined) {
|
||||||
|
metadata.name = track.metadata.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata;
|
||||||
|
};
|
||||||
|
|
||||||
export const intoTimescale = (timeInSeconds: number, timescale: number, round = true) => {
|
export const intoTimescale = (timeInSeconds: number, timescale: number, round = true) => {
|
||||||
const value = timeInSeconds * timescale;
|
const value = timeInSeconds * timescale;
|
||||||
return round ? Math.round(value) : value;
|
return round ? Math.round(value) : value;
|
||||||
|
|||||||
+34
-1
@@ -7,13 +7,22 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { MediaCodec } from '../codec';
|
import { MediaCodec } from '../codec';
|
||||||
|
import { assertNever, textDecoder, textEncoder } from '../misc';
|
||||||
import { Reader } from '../reader';
|
import { Reader } from '../reader';
|
||||||
import { Writer } from '../writer';
|
import { Writer } from '../writer';
|
||||||
|
|
||||||
export interface EBMLElement {
|
export interface EBMLElement {
|
||||||
id: number;
|
id: number;
|
||||||
size?: number;
|
size?: number;
|
||||||
data: number | string | Uint8Array | EBMLFloat32 | EBMLFloat64 | EBMLSignedInt | (EBML | null)[];
|
data:
|
||||||
|
| number
|
||||||
|
| string
|
||||||
|
| Uint8Array
|
||||||
|
| EBMLFloat32
|
||||||
|
| EBMLFloat64
|
||||||
|
| EBMLSignedInt
|
||||||
|
| EBMLUnicodeString
|
||||||
|
| (EBML | null)[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EBML = EBMLElement | Uint8Array | (EBML | null)[];
|
export type EBML = EBMLElement | Uint8Array | (EBML | null)[];
|
||||||
@@ -45,6 +54,10 @@ export class EBMLSignedInt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class EBMLUnicodeString {
|
||||||
|
constructor(public value: string) {}
|
||||||
|
}
|
||||||
|
|
||||||
/** Defines some of the EBML IDs used by Matroska files. */
|
/** Defines some of the EBML IDs used by Matroska files. */
|
||||||
export enum EBMLId {
|
export enum EBMLId {
|
||||||
EBML = 0x1a45dfa3,
|
EBML = 0x1a45dfa3,
|
||||||
@@ -73,6 +86,7 @@ export enum EBMLId {
|
|||||||
FlagDefault = 0x88,
|
FlagDefault = 0x88,
|
||||||
FlagForced = 0x55aa,
|
FlagForced = 0x55aa,
|
||||||
FlagLacing = 0x9c,
|
FlagLacing = 0x9c,
|
||||||
|
Name = 0x536e,
|
||||||
Language = 0x22b59c,
|
Language = 0x22b59c,
|
||||||
CodecID = 0x86,
|
CodecID = 0x86,
|
||||||
CodecPrivate = 0x63a2,
|
CodecPrivate = 0x63a2,
|
||||||
@@ -371,6 +385,12 @@ export class EBMLWriter {
|
|||||||
const size = data.size ?? measureSignedInt(data.data.value);
|
const size = data.size ?? measureSignedInt(data.data.value);
|
||||||
this.writeVarInt(size);
|
this.writeVarInt(size);
|
||||||
this.writeSignedInt(data.data.value, size);
|
this.writeSignedInt(data.data.value, size);
|
||||||
|
} else if (data.data instanceof EBMLUnicodeString) {
|
||||||
|
const bytes = textEncoder.encode(data.data.value);
|
||||||
|
this.writeVarInt(bytes.length);
|
||||||
|
this.writer.write(bytes);
|
||||||
|
} else {
|
||||||
|
assertNever(data.data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -514,6 +534,19 @@ export class EBMLReader {
|
|||||||
return String.fromCharCode(...new Uint8Array(view.buffer, offset, strLength));
|
return String.fromCharCode(...new Uint8Array(view.buffer, offset, strLength));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readUnicodeString(length: number) {
|
||||||
|
const { view, offset } = this.reader.getViewAndOffset(this.pos, this.pos + length);
|
||||||
|
this.pos += length;
|
||||||
|
|
||||||
|
// Actual string length might be shorter due to null terminators
|
||||||
|
let strLength = 0;
|
||||||
|
while (strLength < length && view.getUint8(offset + strLength) !== 0) {
|
||||||
|
strLength += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return textDecoder.decode(new Uint8Array(view.buffer, offset, strLength));
|
||||||
|
}
|
||||||
|
|
||||||
readElementId() {
|
readElementId() {
|
||||||
const size = this.readVarIntSize();
|
const size = this.readVarIntSize();
|
||||||
if (size === null) {
|
if (size === null) {
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ type InternalTrack = {
|
|||||||
codecId: string | null;
|
codecId: string | null;
|
||||||
codecPrivate: Uint8Array | null;
|
codecPrivate: Uint8Array | null;
|
||||||
defaultDuration: number | null;
|
defaultDuration: number | null;
|
||||||
|
name: string | null;
|
||||||
languageCode: string;
|
languageCode: string;
|
||||||
info:
|
info:
|
||||||
| null
|
| null
|
||||||
@@ -836,6 +837,7 @@ export class MatroskaDemuxer extends Demuxer {
|
|||||||
codecId: null,
|
codecId: null,
|
||||||
codecPrivate: null,
|
codecPrivate: null,
|
||||||
defaultDuration: null,
|
defaultDuration: null,
|
||||||
|
name: null,
|
||||||
languageCode: UNDETERMINED_LANGUAGE,
|
languageCode: UNDETERMINED_LANGUAGE,
|
||||||
info: null,
|
info: null,
|
||||||
};
|
};
|
||||||
@@ -1004,6 +1006,12 @@ export class MatroskaDemuxer extends Demuxer {
|
|||||||
= this.currentTrack.segment.timestampFactor * reader.readUnsignedInt(size) / 1e9;
|
= this.currentTrack.segment.timestampFactor * reader.readUnsignedInt(size) / 1e9;
|
||||||
}; break;
|
}; break;
|
||||||
|
|
||||||
|
case EBMLId.Name: {
|
||||||
|
if (!this.currentTrack) break;
|
||||||
|
|
||||||
|
this.currentTrack.name = reader.readUnicodeString(size);
|
||||||
|
}; break;
|
||||||
|
|
||||||
case EBMLId.Language: {
|
case EBMLId.Language: {
|
||||||
if (!this.currentTrack) break;
|
if (!this.currentTrack) break;
|
||||||
|
|
||||||
@@ -1261,6 +1269,10 @@ abstract class MatroskaTrackBacking implements InputTrackBacking {
|
|||||||
return (lastPacket?.timestamp ?? 0) + (lastPacket?.duration ?? 0);
|
return (lastPacket?.timestamp ?? 0) + (lastPacket?.duration ?? 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
return this.internalTrack.name;
|
||||||
|
}
|
||||||
|
|
||||||
getLanguageCode() {
|
getLanguageCode() {
|
||||||
return this.internalTrack.languageCode;
|
return this.internalTrack.languageCode;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import {
|
|||||||
EBMLFloat64,
|
EBMLFloat64,
|
||||||
EBMLId,
|
EBMLId,
|
||||||
EBMLSignedInt,
|
EBMLSignedInt,
|
||||||
|
EBMLUnicodeString,
|
||||||
EBMLWriter,
|
EBMLWriter,
|
||||||
} from './ebml';
|
} from './ebml';
|
||||||
import { buildMatroskaMimeType } from './matroska-misc';
|
import { buildMatroskaMimeType } from './matroska-misc';
|
||||||
@@ -272,6 +273,9 @@ export class MatroskaMuxer extends Muxer {
|
|||||||
{ id: EBMLId.CodecID, data: codecId },
|
{ id: EBMLId.CodecID, data: codecId },
|
||||||
{ id: EBMLId.CodecDelay, data: 0 },
|
{ id: EBMLId.CodecDelay, data: 0 },
|
||||||
{ id: EBMLId.SeekPreRoll, data: seekPreRollNs },
|
{ id: EBMLId.SeekPreRoll, data: seekPreRollNs },
|
||||||
|
trackData.track.metadata.name !== undefined
|
||||||
|
? { id: EBMLId.Name, data: new EBMLUnicodeString(trackData.track.metadata.name) }
|
||||||
|
: null,
|
||||||
(trackData.type === 'video' ? this.videoSpecificTrackInfo(trackData) : null),
|
(trackData.type === 'video' ? this.videoSpecificTrackInfo(trackData) : null),
|
||||||
(trackData.type === 'audio' ? this.audioSpecificTrackInfo(trackData) : null),
|
(trackData.type === 'audio' ? this.audioSpecificTrackInfo(trackData) : null),
|
||||||
(trackData.type === 'subtitle' ? this.subtitleSpecificTrackInfo(trackData) : null),
|
(trackData.type === 'subtitle' ? this.subtitleSpecificTrackInfo(trackData) : null),
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ export const toDataView = (source: AllowSharedBufferSource) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const textDecoder = new TextDecoder();
|
||||||
export const textEncoder = new TextEncoder();
|
export const textEncoder = new TextEncoder();
|
||||||
|
|
||||||
const invertObject = <K extends PropertyKey, V extends PropertyKey>(object: Record<K, V>) => {
|
const invertObject = <K extends PropertyKey, V extends PropertyKey>(object: Record<K, V>) => {
|
||||||
|
|||||||
@@ -168,6 +168,10 @@ class Mp3AudioTrackBacking implements InputAudioTrackBacking {
|
|||||||
return (lastPacket?.timestamp ?? 0) + (lastPacket?.duration ?? 0);
|
return (lastPacket?.timestamp ?? 0) + (lastPacket?.duration ?? 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
getLanguageCode() {
|
getLanguageCode() {
|
||||||
return UNDETERMINED_LANGUAGE;
|
return UNDETERMINED_LANGUAGE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -442,6 +442,10 @@ class OggAudioTrackBacking implements InputAudioTrackBacking {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
getLanguageCode() {
|
getLanguageCode() {
|
||||||
return UNDETERMINED_LANGUAGE;
|
return UNDETERMINED_LANGUAGE;
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -67,6 +67,8 @@ export type OutputSubtitleTrack = OutputTrack & { type: 'subtitle' };
|
|||||||
export type BaseTrackMetadata = {
|
export type BaseTrackMetadata = {
|
||||||
/** The three-letter, ISO 639-2/T language code specifying the language of this track. */
|
/** The three-letter, ISO 639-2/T language code specifying the language of this track. */
|
||||||
languageCode?: string;
|
languageCode?: string;
|
||||||
|
/** A user-defined name for this track, like "English" or "Director Commentary". */
|
||||||
|
name?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,7 +101,10 @@ const validateBaseTrackMetadata = (metadata: BaseTrackMetadata) => {
|
|||||||
throw new TypeError('metadata must be an object.');
|
throw new TypeError('metadata must be an object.');
|
||||||
}
|
}
|
||||||
if (metadata.languageCode !== undefined && !isIso639Dash2LanguageCode(metadata.languageCode)) {
|
if (metadata.languageCode !== undefined && !isIso639Dash2LanguageCode(metadata.languageCode)) {
|
||||||
throw new TypeError('metadata.languageCode must be a three-letter, ISO 639-2/T language code.');
|
throw new TypeError('metadata.languageCode, when provided, must be a three-letter, ISO 639-2/T language code.');
|
||||||
|
}
|
||||||
|
if (metadata.name !== undefined && typeof metadata.name !== 'string') {
|
||||||
|
throw new TypeError('metadata.name, when provided, must be a string.');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -255,6 +255,10 @@ class WaveAudioTrackBacking implements InputAudioTrackBacking {
|
|||||||
return this.demuxer.audioInfo.sampleRate;
|
return this.demuxer.audioInfo.sampleRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
getLanguageCode() {
|
getLanguageCode() {
|
||||||
return UNDETERMINED_LANGUAGE;
|
return UNDETERMINED_LANGUAGE;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user