mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-10-02 21:33:52 +02:00
Add media conversion utility function, fix fragment/cluster reading issues, expand color space logic, add qualitative bitrates, & many other fixes and improvements
This commit is contained in:
@@ -450,7 +450,7 @@ export class EBMLReader {
|
||||
}
|
||||
|
||||
if (width !== 4 && width !== 8) {
|
||||
throw new Error('Bad FLOAT size ' + width);
|
||||
throw new Error('Bad float size ' + width);
|
||||
}
|
||||
|
||||
const { view, offset } = this.reader.getViewAndOffset(this.pos, this.pos + width);
|
||||
|
||||
@@ -69,6 +69,7 @@ type Cluster = {
|
||||
timestamp: number;
|
||||
trackData: Map<number, ClusterTrackData>;
|
||||
nextCluster: Cluster | null;
|
||||
isKnownToBeFirstCluster: boolean;
|
||||
};
|
||||
|
||||
type ClusterTrackData = {
|
||||
@@ -415,6 +416,7 @@ export class MatroskaDemuxer extends Demuxer {
|
||||
timestamp: -1,
|
||||
trackData: new Map(),
|
||||
nextCluster: null,
|
||||
isKnownToBeFirstCluster: false,
|
||||
};
|
||||
this.currentCluster = cluster;
|
||||
this.readContiguousElements(this.clusterReader, size);
|
||||
@@ -772,7 +774,7 @@ export class MatroskaDemuxer extends Demuxer {
|
||||
|
||||
const matrixCoefficients = reader.readUnsignedInt(size);
|
||||
const mapped = MATRIX_COEFFICIENTS_MAP_INVERSE[matrixCoefficients] ?? null;
|
||||
this.currentTrack.info.colorSpace.matrix = mapped;
|
||||
this.currentTrack.info.colorSpace.matrix = mapped as VideoColorSpaceInit['matrix'];
|
||||
}; break;
|
||||
|
||||
case EBMLId.Range: {
|
||||
@@ -786,7 +788,7 @@ export class MatroskaDemuxer extends Demuxer {
|
||||
|
||||
const transferCharacteristics = reader.readUnsignedInt(size);
|
||||
const mapped = TRANSFER_CHARACTERISTICS_MAP_INVERSE[transferCharacteristics] ?? null;
|
||||
this.currentTrack.info.colorSpace.transfer = mapped;
|
||||
this.currentTrack.info.colorSpace.transfer = mapped as VideoColorSpaceInit['transfer'];
|
||||
}; break;
|
||||
|
||||
case EBMLId.Primaries: {
|
||||
@@ -794,7 +796,7 @@ export class MatroskaDemuxer extends Demuxer {
|
||||
|
||||
const primaries = reader.readUnsignedInt(size);
|
||||
const mapped = COLOR_PRIMARIES_MAP_INVERSE[primaries] ?? null;
|
||||
this.currentTrack.info.colorSpace.primaries = mapped;
|
||||
this.currentTrack.info.colorSpace.primaries = mapped as VideoColorSpaceInit['primaries'];
|
||||
}; break;
|
||||
|
||||
case EBMLId.Projection: {
|
||||
@@ -967,6 +969,10 @@ abstract class MatroskaTrackBacking<
|
||||
|
||||
constructor(public internalTrack: InternalTrack) {}
|
||||
|
||||
getId() {
|
||||
return this.internalTrack.id;
|
||||
}
|
||||
|
||||
getCodec(): Promise<MediaCodec | null> {
|
||||
throw new Error('Not implemented on base class.');
|
||||
}
|
||||
@@ -996,14 +1002,35 @@ abstract class MatroskaTrackBacking<
|
||||
async getFirstSample(options: SampleRetrievalOptions) {
|
||||
return this.performClusterLookup(
|
||||
() => {
|
||||
const cluster = this.internalTrack.clusters[0];
|
||||
const startCluster = this.internalTrack.segment.clusters[0] ?? null;
|
||||
if (startCluster?.isKnownToBeFirstCluster) {
|
||||
// Walk from the very first cluster in the file until we find one with our track in it
|
||||
let currentCluster: Cluster | null = startCluster;
|
||||
while (currentCluster) {
|
||||
const trackData = currentCluster.trackData.get(this.internalTrack.id);
|
||||
if (trackData) {
|
||||
return {
|
||||
clusterIndex: binarySearchExact(
|
||||
this.internalTrack.clusters,
|
||||
currentCluster.elementStartPos,
|
||||
x => x.elementStartPos,
|
||||
),
|
||||
blockIndex: 0,
|
||||
correctBlockFound: true,
|
||||
};
|
||||
}
|
||||
|
||||
currentCluster = currentCluster.nextCluster;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
clusterIndex: cluster ? 0 : -1,
|
||||
blockIndex: cluster ? 0 : -1,
|
||||
correctBlockFound: !!cluster,
|
||||
clusterIndex: -1,
|
||||
blockIndex: -1,
|
||||
correctBlockFound: false,
|
||||
};
|
||||
},
|
||||
0,
|
||||
-Infinity, // Use -Infinity as a search timestamp to avoid using the cues
|
||||
Infinity,
|
||||
options,
|
||||
);
|
||||
@@ -1300,8 +1327,11 @@ abstract class MatroskaTrackBacking<
|
||||
);
|
||||
const cuePoint = cuePointIndex !== -1 ? this.internalTrack.cuePoints[cuePointIndex]! : null;
|
||||
|
||||
let nextClusterIsFirstCluster = false;
|
||||
|
||||
if (clusterIndex === -1) {
|
||||
metadataReader.pos = cuePoint?.clusterPosition ?? segment.clusterSeekStartPos;
|
||||
nextClusterIsFirstCluster = metadataReader.pos === segment.clusterSeekStartPos;
|
||||
} else {
|
||||
const cluster = this.internalTrack.clusters[clusterIndex]!;
|
||||
|
||||
@@ -1344,15 +1374,18 @@ abstract class MatroskaTrackBacking<
|
||||
// This is the first time we've seen this cluster
|
||||
metadataReader.pos = elementStartPos;
|
||||
cluster = await demuxer.readCluster(segment);
|
||||
|
||||
if (prevCluster) prevCluster.nextCluster = cluster;
|
||||
prevCluster = cluster;
|
||||
} else {
|
||||
// We already know this cluster
|
||||
cluster = segment.clusters[index]!;
|
||||
// Even if we already know the cluster, we might not yet know its predecessor
|
||||
if (prevCluster) prevCluster.nextCluster = cluster;
|
||||
prevCluster = cluster;
|
||||
}
|
||||
|
||||
// Even if we already know the cluster, we might not yet know its predecessor, so always do this
|
||||
if (prevCluster) prevCluster.nextCluster = cluster;
|
||||
prevCluster = cluster;
|
||||
|
||||
if (nextClusterIsFirstCluster) {
|
||||
cluster.isKnownToBeFirstCluster = true;
|
||||
nextClusterIsFirstCluster = false;
|
||||
}
|
||||
|
||||
const { clusterIndex, blockIndex, correctBlockFound } = getBestMatch();
|
||||
@@ -1417,6 +1450,15 @@ class MatroskaVideoTrackBacking extends MatroskaTrackBacking<EncodedVideoSample>
|
||||
return this.internalTrack.info.rotation;
|
||||
}
|
||||
|
||||
async getColorSpace(): Promise<VideoColorSpaceInit> {
|
||||
return {
|
||||
primaries: this.internalTrack.info.colorSpace?.primaries,
|
||||
transfer: this.internalTrack.info.colorSpace?.transfer,
|
||||
matrix: this.internalTrack.info.colorSpace?.matrix,
|
||||
fullRange: this.internalTrack.info.colorSpace?.fullRange,
|
||||
};
|
||||
}
|
||||
|
||||
async getDecoderConfig(): Promise<VideoDecoderConfig | null> {
|
||||
if (!this.internalTrack.info.codec) {
|
||||
return null;
|
||||
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
parseSubtitleTimestamp,
|
||||
} from '../subtitles';
|
||||
import {
|
||||
PCM_CODECS,
|
||||
PCM_AUDIO_CODECS,
|
||||
PcmAudioCodec,
|
||||
generateAv1CodecConfigurationFromCodecString,
|
||||
parsePcmCodec,
|
||||
@@ -308,7 +308,7 @@ export class MatroskaMuxer extends Muxer {
|
||||
},
|
||||
{
|
||||
id: EBMLId.ProjectionPoseRoll,
|
||||
data: (rotation + 180) % 360 - 180, // [0, 270] -> [-180, 90]
|
||||
data: new EBMLFloat32((rotation + 180) % 360 - 180), // [0, 270] -> [-180, 90]
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -321,7 +321,7 @@ export class MatroskaMuxer extends Muxer {
|
||||
}
|
||||
|
||||
private audioSpecificTrackInfo(trackData: MatroskaAudioTrackData) {
|
||||
const pcmInfo = (PCM_CODECS as readonly string[]).includes(trackData.track.source._codec)
|
||||
const pcmInfo = (PCM_AUDIO_CODECS as readonly string[]).includes(trackData.track.source._codec)
|
||||
? parsePcmCodec(trackData.track.source._codec as PcmAudioCodec)
|
||||
: null;
|
||||
|
||||
@@ -659,16 +659,16 @@ export class MatroskaMuxer extends Muxer {
|
||||
// We can only finalize this cluster (and begin a new one) if we know that each track will be able to
|
||||
// start the new one with a key frame.
|
||||
const keyFrameQueuedEverywhere = this.trackDatas.every((otherTrackData) => {
|
||||
if (otherTrackData.track.source._closed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (trackData === otherTrackData) {
|
||||
return chunk.type === 'key';
|
||||
}
|
||||
|
||||
const firstQueuedSample = otherTrackData.chunkQueue[0];
|
||||
return firstQueuedSample && firstQueuedSample.type === 'key';
|
||||
if (firstQueuedSample) {
|
||||
return firstQueuedSample.type === 'key';
|
||||
}
|
||||
|
||||
return otherTrackData.track.source._closed;
|
||||
});
|
||||
|
||||
if (
|
||||
|
||||
Reference in New Issue
Block a user