mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-09-27 19:03:46 +02:00
Multiple miscellaneous fixes
This commit is contained in:
+3
-3
@@ -641,7 +641,7 @@ class Conversion {
|
||||
}
|
||||
|
||||
if (needsResample) {
|
||||
audioSource = await this.resampleAudio(track, codecOfChoice, numberOfChannels, sampleRate);
|
||||
audioSource = this.resampleAudio(track, codecOfChoice, numberOfChannels, sampleRate);
|
||||
} else {
|
||||
const source = new AudioDataSource({
|
||||
codec: codecOfChoice,
|
||||
@@ -654,7 +654,7 @@ class Conversion {
|
||||
await this.started;
|
||||
|
||||
const sink = new AudioDataSink(track);
|
||||
for await (const { data, timestamp } of sink.data(undefined, this.startTimestamp)) {
|
||||
for await (const { data, timestamp } of sink.data(undefined, this.endTimestamp)) {
|
||||
if (this.synchronizer.shouldWait(track.id, timestamp)) {
|
||||
await this.synchronizer.wait(timestamp);
|
||||
}
|
||||
@@ -686,7 +686,7 @@ class Conversion {
|
||||
* Resamples the audio by decoding it, playing it onto an OfflineAudioContext and encoding the
|
||||
* resulting AudioBuffer.
|
||||
*/
|
||||
async resampleAudio(
|
||||
resampleAudio(
|
||||
track: InputAudioTrack,
|
||||
codec: AudioCodec,
|
||||
targetNumberOfChannels: number,
|
||||
|
||||
@@ -27,7 +27,6 @@ import {
|
||||
COLOR_PRIMARIES_MAP_INVERSE,
|
||||
MATRIX_COEFFICIENTS_MAP_INVERSE,
|
||||
TRANSFER_CHARACTERISTICS_MAP_INVERSE,
|
||||
rotationMatrix,
|
||||
binarySearchLessOrEqual,
|
||||
binarySearchExact,
|
||||
Rotation,
|
||||
@@ -38,6 +37,8 @@ import {
|
||||
TransformationMatrix,
|
||||
extractRotationFromMatrix,
|
||||
roundToPrecision,
|
||||
isIso639Dash2LanguageCode,
|
||||
roundToMultiple,
|
||||
} from '../misc';
|
||||
import { Reader } from '../reader';
|
||||
import { EncodedAudioSample, EncodedVideoSample, PLACEHOLDER_DATA, SampleType } from '../sample';
|
||||
@@ -175,8 +176,6 @@ type Fragment = {
|
||||
isKnownToBeFirstFragment: boolean;
|
||||
};
|
||||
|
||||
const knownMatrixes = [rotationMatrix(0), rotationMatrix(90), rotationMatrix(180), rotationMatrix(270)];
|
||||
|
||||
export class IsobmffDemuxer extends Demuxer {
|
||||
metadataReader: IsobmffReader;
|
||||
currentTrack: InternalTrack | null = null;
|
||||
@@ -657,16 +656,11 @@ export class IsobmffDemuxer extends Demuxer {
|
||||
this.metadataReader.readFixed_2_30(),
|
||||
];
|
||||
|
||||
const rotation = extractRotationFromMatrix(matrix);
|
||||
const comparisonMatrix = rotationMatrix(rotation);
|
||||
const rotation = (roundToMultiple(extractRotationFromMatrix(matrix), 90) + 360) % 360 as Rotation;
|
||||
assert(rotation === 0 || rotation === 90 || rotation === 180 || rotation === 270);
|
||||
|
||||
const matrixIndex = knownMatrixes.findIndex(mat => mat.every((y, i) => y === comparisonMatrix[i]));
|
||||
if (matrixIndex === -1) {
|
||||
console.warn(`Wacky rotation matrix ${comparisonMatrix.join(',')}; sticking with no rotation.`);
|
||||
track.rotation = 0;
|
||||
} else {
|
||||
track.rotation = (90 * matrixIndex) as Rotation;
|
||||
}
|
||||
// Flip clockwise to counter-clockwise
|
||||
track.rotation = (-rotation + 360) % 360 as Rotation;
|
||||
}; break;
|
||||
|
||||
case 'elst': {
|
||||
@@ -739,6 +733,11 @@ export class IsobmffDemuxer extends Demuxer {
|
||||
track.languageCode = String.fromCharCode(0x60 + (language & 0b11111)) + track.languageCode;
|
||||
language >>= 5;
|
||||
}
|
||||
|
||||
if (!isIso639Dash2LanguageCode(track.languageCode)) {
|
||||
// Sometimes the bytes are garbage
|
||||
track.languageCode = UNDETERMINED_LANGUAGE;
|
||||
}
|
||||
}
|
||||
}; break;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
validateSubtitleMetadata,
|
||||
validateVideoChunkMetadata,
|
||||
} from '../codec';
|
||||
import { EncodedAudioSample, EncodedVideoSample } from '../sample';
|
||||
import { EncodedAudioSample, EncodedVideoSample, SampleType } from '../sample';
|
||||
import { BufferTarget } from '../target';
|
||||
|
||||
export const GLOBAL_TIMESCALE = 1000;
|
||||
@@ -25,7 +25,7 @@ export type Sample = {
|
||||
duration: number;
|
||||
data: Uint8Array | null;
|
||||
size: number;
|
||||
type: 'key' | 'delta';
|
||||
type: SampleType;
|
||||
timescaleUnitsToNextSample: number;
|
||||
};
|
||||
|
||||
@@ -492,7 +492,7 @@ export class IsobmffMuxer extends Muxer {
|
||||
data: Uint8Array,
|
||||
timestamp: number,
|
||||
duration: number,
|
||||
type: 'key' | 'delta',
|
||||
type: SampleType,
|
||||
) {
|
||||
const sample: Sample = {
|
||||
timestamp,
|
||||
@@ -564,6 +564,8 @@ export class IsobmffMuxer extends Muxer {
|
||||
|
||||
const timescaleUnits = intoTimescale(sample.decodeTimestamp, trackData.timescale, false);
|
||||
const delta = Math.round(timescaleUnits - trackData.lastTimescaleUnits);
|
||||
assert(delta >= 0);
|
||||
|
||||
trackData.lastTimescaleUnits += delta;
|
||||
trackData.lastSample.timescaleUnitsToNextSample = delta;
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
binarySearchLessOrEqual,
|
||||
COLOR_PRIMARIES_MAP_INVERSE,
|
||||
findLastIndex,
|
||||
isIso639Dash2LanguageCode,
|
||||
last,
|
||||
MATRIX_COEFFICIENTS_MAP_INVERSE,
|
||||
Rotation,
|
||||
@@ -749,6 +750,10 @@ export class MatroskaDemuxer extends Demuxer {
|
||||
if (!this.currentTrack) break;
|
||||
|
||||
this.currentTrack.languageCode = reader.readString(size);
|
||||
|
||||
if (!isIso639Dash2LanguageCode(this.currentTrack.languageCode)) {
|
||||
this.currentTrack.languageCode = UNDETERMINED_LANGUAGE;
|
||||
}
|
||||
}; break;
|
||||
|
||||
case EBMLId.Video: {
|
||||
|
||||
@@ -687,8 +687,8 @@ export class MatroskaMuxer extends Muxer {
|
||||
}
|
||||
|
||||
const relativeTimestamp = msTimestamp - this.currentClusterStartMsTimestamp!;
|
||||
if (relativeTimestamp < 0) {
|
||||
// The chunk lies outside of the current cluster
|
||||
if (relativeTimestamp < -(2 ** 15)) {
|
||||
// The block lies too far in the past, it's not representable within this cluster
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -393,6 +393,10 @@ export const roundToPrecision = (value: number, digits: number) => {
|
||||
return Math.round(value * factor) / factor;
|
||||
};
|
||||
|
||||
export const roundToMultiple = (value: number, multiple: number) => {
|
||||
return Math.round(value / multiple) * multiple;
|
||||
};
|
||||
|
||||
export const ilog = (x: number) => {
|
||||
let ret = 0;
|
||||
while (x) {
|
||||
@@ -401,3 +405,8 @@ export const ilog = (x: number) => {
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
const ISO_639_2_REGEX = /^[a-z]{3}$/;
|
||||
export const isIso639Dash2LanguageCode = (x: string) => {
|
||||
return ISO_639_2_REGEX.test(x);
|
||||
};
|
||||
|
||||
+10
-16
@@ -31,7 +31,7 @@ export abstract class Muxer {
|
||||
private trackTimestampInfo = new WeakMap<OutputTrack, {
|
||||
timestampOffset: number;
|
||||
maxTimestamp: number;
|
||||
lastKeyFrameTimestamp: number;
|
||||
maxTimestampBeforeLastKeyFrame: number;
|
||||
}>();
|
||||
|
||||
protected validateAndNormalizeTimestamp(track: OutputTrack, timestampInSeconds: number, isKeyFrame: boolean) {
|
||||
@@ -44,7 +44,7 @@ export abstract class Muxer {
|
||||
timestampInfo = {
|
||||
timestampOffset: timestampInSeconds,
|
||||
maxTimestamp: track.source._offsetTimestamps ? 0 : timestampInSeconds,
|
||||
lastKeyFrameTimestamp: track.source._offsetTimestamps ? 0 : timestampInSeconds,
|
||||
maxTimestampBeforeLastKeyFrame: track.source._offsetTimestamps ? 0 : timestampInSeconds,
|
||||
};
|
||||
this.trackTimestampInfo.set(track, timestampInfo);
|
||||
}
|
||||
@@ -57,22 +57,16 @@ export abstract class Muxer {
|
||||
throw new Error(`Timestamps must be non-negative (got ${timestampInSeconds}s).`);
|
||||
}
|
||||
|
||||
if (timestampInSeconds < timestampInfo.lastKeyFrameTimestamp) {
|
||||
throw new Error(
|
||||
`Timestamp cannot be smaller than last key frame's timestamp (got ${timestampInSeconds}s,`
|
||||
+ ` last key frame at ${timestampInfo.lastKeyFrameTimestamp}s).`,
|
||||
);
|
||||
if (isKeyFrame) {
|
||||
timestampInfo.maxTimestampBeforeLastKeyFrame = timestampInfo.maxTimestamp;
|
||||
}
|
||||
|
||||
if (isKeyFrame) {
|
||||
if (timestampInSeconds < timestampInfo.maxTimestamp) {
|
||||
throw new Error(
|
||||
`Key frame timestamps cannot be smaller than any timestamp that came before`
|
||||
+ ` (got ${timestampInSeconds}s, max timestamp was ${timestampInfo.maxTimestamp}s).`,
|
||||
);
|
||||
}
|
||||
|
||||
timestampInfo.lastKeyFrameTimestamp = timestampInSeconds;
|
||||
if (timestampInSeconds < timestampInfo.maxTimestampBeforeLastKeyFrame) {
|
||||
throw new Error(
|
||||
`Timestamps cannot be smaller than the highest timestamp of the previous run (a run begins with a`
|
||||
+ ` key frame and ends right before the next key frame). Got ${timestampInSeconds}s, but highest`
|
||||
+ ` timestamp is ${timestampInfo.maxTimestampBeforeLastKeyFrame}s.`,
|
||||
);
|
||||
}
|
||||
|
||||
timestampInfo.maxTimestamp = Math.max(timestampInfo.maxTimestamp, timestampInSeconds);
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import { AsyncMutex, TransformationMatrix } from './misc';
|
||||
import { AsyncMutex, isIso639Dash2LanguageCode, TransformationMatrix } from './misc';
|
||||
import { Muxer } from './muxer';
|
||||
import { OutputFormat } from './output-format';
|
||||
import { AudioSource, MediaSource, SubtitleSource, VideoSource } from './media-source';
|
||||
@@ -60,7 +60,7 @@ const validateBaseTrackMetadata = (metadata: BaseTrackMetadata) => {
|
||||
if (!metadata || typeof metadata !== 'object') {
|
||||
throw new TypeError('metadata must be an object.');
|
||||
}
|
||||
if (metadata.languageCode !== undefined && !/^[a-z]{3}$/.test(metadata.languageCode)) {
|
||||
if (metadata.languageCode !== undefined && !isIso639Dash2LanguageCode(metadata.languageCode)) {
|
||||
throw new TypeError('metadata.languageCode must be a three-letter, ISO 639-2 language code.');
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user