mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-10-01 04:43:51 +02:00
Add getMimeType to Output & other small adjustments
This commit is contained in:
@@ -49,6 +49,7 @@ import {
|
||||
} from '../misc';
|
||||
import { EncodedPacket, PLACEHOLDER_DATA } from '../packet';
|
||||
import { Reader } from '../reader';
|
||||
import { buildIsobmffMimeType } from './isobmff-misc';
|
||||
import { IsobmffReader, MAX_BOX_HEADER_SIZE } from './isobmff-reader';
|
||||
|
||||
type InternalTrack = {
|
||||
@@ -223,22 +224,14 @@ export class IsobmffDemuxer extends Demuxer {
|
||||
override async getMimeType() {
|
||||
await this.readMetadata();
|
||||
|
||||
const base = this.tracks.some(x => x.info?.type === 'video')
|
||||
? 'video/'
|
||||
: this.tracks.some(x => x.info?.type === 'audio')
|
||||
? 'audio/'
|
||||
: 'application/';
|
||||
const codecStrings = await Promise.all(this.tracks.map(x => x.inputTrack!.getCodecParameterString()));
|
||||
|
||||
let string = base + (this.isQuickTime ? 'quicktime' : 'mp4');
|
||||
|
||||
if (this.tracks.length > 0) {
|
||||
const codecMimeTypes = await Promise.all(this.tracks.map(x => x.inputTrack!.getCodecParameterString()));
|
||||
const uniqueCodecMimeTypes = [...new Set(codecMimeTypes.filter(Boolean))];
|
||||
|
||||
string += `; codecs="${uniqueCodecMimeTypes.join(', ')}"`;
|
||||
}
|
||||
|
||||
return string;
|
||||
return buildIsobmffMimeType({
|
||||
isQuicktime: this.isQuickTime,
|
||||
hasVideo: this.tracks.some(x => x.info?.type === 'video'),
|
||||
hasAudio: this.tracks.some(x => x.info?.type === 'audio'),
|
||||
codecStrings: codecStrings.filter(Boolean) as string[],
|
||||
});
|
||||
}
|
||||
|
||||
readMetadata() {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
export const buildIsobmffMimeType = (info: {
|
||||
isQuicktime: boolean;
|
||||
hasVideo: boolean;
|
||||
hasAudio: boolean;
|
||||
codecStrings: string[];
|
||||
}) => {
|
||||
const base = info.hasVideo
|
||||
? 'video/'
|
||||
: info.hasAudio
|
||||
? 'audio/'
|
||||
: 'application/';
|
||||
|
||||
let string = base + (info.isQuicktime ? 'quicktime' : 'mp4');
|
||||
|
||||
if (info.codecStrings.length > 0) {
|
||||
const uniqueCodecMimeTypes = [...new Set(info.codecStrings)];
|
||||
string += `; codecs="${uniqueCodecMimeTypes.join(', ')}"`;
|
||||
}
|
||||
|
||||
return string;
|
||||
};
|
||||
@@ -2,13 +2,14 @@ import { Box, ftyp, IsobmffBoxWriter, mdat, mfra, moof, moov, vtta, vttc, vtte }
|
||||
import { Muxer } from '../muxer';
|
||||
import { Output, OutputAudioTrack, OutputSubtitleTrack, OutputTrack, OutputVideoTrack } from '../output';
|
||||
import { BufferTargetWriter, Writer } from '../writer';
|
||||
import { assert, computeRationalApproximation, last } from '../misc';
|
||||
import { assert, computeRationalApproximation, last, promiseWithResolvers } from '../misc';
|
||||
import { IsobmffOutputFormatOptions, IsobmffOutputFormat, MovOutputFormat } from '../output-format';
|
||||
import { inlineTimestampRegex, SubtitleConfig, SubtitleCue, SubtitleMetadata } from '../subtitles';
|
||||
import {
|
||||
parsePcmCodec,
|
||||
PCM_AUDIO_CODECS,
|
||||
PcmAudioCodec,
|
||||
SubtitleCodec,
|
||||
validateAudioChunkMetadata,
|
||||
validateSubtitleMetadata,
|
||||
validateVideoChunkMetadata,
|
||||
@@ -22,6 +23,7 @@ import {
|
||||
serializeHevcDecoderConfigurationRecord,
|
||||
transformAnnexBToLengthPrefixed,
|
||||
} from '../codec-data';
|
||||
import { buildIsobmffMimeType } from './isobmff-misc';
|
||||
|
||||
export const GLOBAL_TIMESCALE = 1000;
|
||||
const TIMESTAMP_OFFSET = 2_082_844_800; // Seconds between Jan 1 1904 and Jan 1 1970
|
||||
@@ -125,6 +127,7 @@ export class IsobmffMuxer extends Muxer {
|
||||
private mdat: Box | null = null;
|
||||
|
||||
private trackDatas: IsobmffTrackData[] = [];
|
||||
private allTracksKnown = promiseWithResolvers();
|
||||
|
||||
private creationTime = Math.floor(Date.now() / 1000) + TIMESTAMP_OFFSET;
|
||||
private finalizedChunks: Chunk[] = [];
|
||||
@@ -197,6 +200,40 @@ export class IsobmffMuxer extends Muxer {
|
||||
release();
|
||||
}
|
||||
|
||||
private allTracksAreKnown() {
|
||||
for (const track of this.output._tracks) {
|
||||
if (!track.source._closed && !this.trackDatas.some(x => x.track === track)) {
|
||||
return false; // We haven't seen a sample from this open track yet
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async getMimeType() {
|
||||
await this.allTracksKnown.promise;
|
||||
|
||||
const codecStrings = this.trackDatas.map((trackData) => {
|
||||
if (trackData.type === 'video') {
|
||||
return trackData.info.decoderConfig.codec;
|
||||
} else if (trackData.type === 'audio') {
|
||||
return trackData.info.decoderConfig.codec;
|
||||
} else {
|
||||
const map: Record<SubtitleCodec, string> = {
|
||||
webvtt: 'wvtt',
|
||||
};
|
||||
return map[trackData.track.source._codec];
|
||||
}
|
||||
});
|
||||
|
||||
return buildIsobmffMimeType({
|
||||
isQuicktime: this.isMov,
|
||||
hasVideo: this.trackDatas.some(x => x.type === 'video'),
|
||||
hasAudio: this.trackDatas.some(x => x.type === 'audio'),
|
||||
codecStrings,
|
||||
});
|
||||
}
|
||||
|
||||
private getVideoTrackData(track: OutputVideoTrack, packet: EncodedPacket, meta?: EncodedVideoChunkMetadata) {
|
||||
const existingTrackData = this.trackDatas.find(x => x.track === track);
|
||||
if (existingTrackData) {
|
||||
@@ -278,6 +315,10 @@ export class IsobmffMuxer extends Muxer {
|
||||
this.trackDatas.push(newTrackData);
|
||||
this.trackDatas.sort((a, b) => a.track.id - b.track.id);
|
||||
|
||||
if (this.allTracksAreKnown()) {
|
||||
this.allTracksKnown.resolve();
|
||||
}
|
||||
|
||||
return newTrackData;
|
||||
}
|
||||
|
||||
@@ -319,6 +360,10 @@ export class IsobmffMuxer extends Muxer {
|
||||
this.trackDatas.push(newTrackData);
|
||||
this.trackDatas.sort((a, b) => a.track.id - b.track.id);
|
||||
|
||||
if (this.allTracksAreKnown()) {
|
||||
this.allTracksKnown.resolve();
|
||||
}
|
||||
|
||||
return newTrackData;
|
||||
}
|
||||
|
||||
@@ -360,6 +405,10 @@ export class IsobmffMuxer extends Muxer {
|
||||
this.trackDatas.push(newTrackData);
|
||||
this.trackDatas.sort((a, b) => a.track.id - b.track.id);
|
||||
|
||||
if (this.allTracksAreKnown()) {
|
||||
this.allTracksKnown.resolve();
|
||||
}
|
||||
|
||||
return newTrackData;
|
||||
}
|
||||
|
||||
@@ -869,10 +918,8 @@ export class IsobmffMuxer extends Muxer {
|
||||
assert(this.isFragmented);
|
||||
|
||||
if (!isFinalCall) {
|
||||
for (const track of this.output._tracks) {
|
||||
if (!track.source._closed && !this.trackDatas.some(x => x.track === track)) {
|
||||
return; // We haven't seen a sample from this open track yet
|
||||
}
|
||||
if (!this.allTracksAreKnown()) {
|
||||
return; // We can't interleave yet as we don't yet know how many tracks we'll truly have
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1006,6 +1053,10 @@ export class IsobmffMuxer extends Muxer {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.allTracksAreKnown()) {
|
||||
this.allTracksKnown.resolve();
|
||||
}
|
||||
|
||||
if (this.isFragmented) {
|
||||
// Since a track is now closed, we may be able to write out chunks that were previously waiting
|
||||
await this.interleaveSamples();
|
||||
@@ -1018,6 +1069,8 @@ export class IsobmffMuxer extends Muxer {
|
||||
async finalize() {
|
||||
const release = await this.mutex.acquire();
|
||||
|
||||
this.allTracksKnown.resolve();
|
||||
|
||||
for (const trackData of this.trackDatas) {
|
||||
if (trackData.type === 'subtitle' && trackData.track.source._codec === 'webvtt') {
|
||||
await this.processWebVTTCues(trackData, Infinity);
|
||||
|
||||
Reference in New Issue
Block a user