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