add codecs
This commit is contained in:
parent
54edfd2fdc
commit
53f2bc8ca7
BIN
apps/mock/public/video/bear-vp9.webm
Normal file
BIN
apps/mock/public/video/bear-vp9.webm
Normal file
Binary file not shown.
@ -4,6 +4,6 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
<my-element />
|
<my-element />
|
||||||
<!-- <video-pipeline-demo src="/api/static/video/test.webm" ></video-pipeline-demo> -->
|
<video-pipeline-demo src="/api/static/video/test.webm"></video-pipeline-demo>
|
||||||
<video-pipeline-demo src="/api/static/video/huge/[LoliHouse] Amagami-san Chi no Enmusubi - 23 [WebRip 1080p HEVC-10bit AAC SRTx2].mkv" width="800" height="450" />
|
<!-- <video-pipeline-demo src="/api/static/video/huge/[LoliHouse] Amagami-san Chi no Enmusubi - 23 [WebRip 1080p HEVC-10bit AAC SRTx2].mkv" width="800" height="450" /> -->
|
||||||
</body>
|
</body>
|
32
apps/playground/src/media/base/audio_codecs.ts
Normal file
32
apps/playground/src/media/base/audio_codecs.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
export enum AudioCodec {
|
||||||
|
Unknown = 0,
|
||||||
|
AAC = 1,
|
||||||
|
MP3 = 2,
|
||||||
|
PCM = 3,
|
||||||
|
Vorbis = 4,
|
||||||
|
FLAC = 5,
|
||||||
|
AMR_NB = 6,
|
||||||
|
AMR_WB = 7,
|
||||||
|
PCM_MULAW = 8,
|
||||||
|
GSM_MS = 9,
|
||||||
|
PCM_S16BE = 10,
|
||||||
|
PCM_S24BE = 11,
|
||||||
|
Opus = 12,
|
||||||
|
EAC3 = 13,
|
||||||
|
PCM_ALAW = 14,
|
||||||
|
ALAC = 15,
|
||||||
|
AC3 = 16,
|
||||||
|
MpegHAudio = 17,
|
||||||
|
DTS = 18,
|
||||||
|
DTSXP2 = 19,
|
||||||
|
DTSE = 20,
|
||||||
|
AC4 = 21,
|
||||||
|
IAMF = 22,
|
||||||
|
PCM_S32BE = 23,
|
||||||
|
PCM_S32LE = 24,
|
||||||
|
PCM_S24LE = 25,
|
||||||
|
PCM_S16LE = 26,
|
||||||
|
PCM_F32BE = 27,
|
||||||
|
PCM_F32LE = 28,
|
||||||
|
MaxValue = PCM_F32LE, // Must equal the last "real" codec above.
|
||||||
|
}
|
11
apps/playground/src/media/base/errors.ts
Normal file
11
apps/playground/src/media/base/errors.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export class UnsupportCodecError extends Error {
|
||||||
|
constructor(codec: string, context: string) {
|
||||||
|
super(`codec ${codec} is not supported in ${context} context`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ParseCodecPrivateError extends Error {
|
||||||
|
constructor(codec: string, detail: string) {
|
||||||
|
super(`code ${codec} private parse failed: ${detail}`);
|
||||||
|
}
|
||||||
|
}
|
97
apps/playground/src/media/base/video_codecs.ts
Normal file
97
apps/playground/src/media/base/video_codecs.ts
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
export enum VideoCodec {
|
||||||
|
Unknown = 0,
|
||||||
|
H264 = 1,
|
||||||
|
VC1 = 2,
|
||||||
|
MPEG2 = 3,
|
||||||
|
MPEG4 = 4,
|
||||||
|
Theora = 5,
|
||||||
|
VP8 = 6,
|
||||||
|
VP9 = 7,
|
||||||
|
HEVC = 8,
|
||||||
|
DolbyVision = 9,
|
||||||
|
AV1 = 10,
|
||||||
|
MaxValue = AV1, // Must equal the last "real" codec above.
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum VideoCodecProfile {
|
||||||
|
VIDEO_CODEC_PROFILE_UNKNOWN = -1,
|
||||||
|
VIDEO_CODEC_PROFILE_MIN = VIDEO_CODEC_PROFILE_UNKNOWN,
|
||||||
|
H264PROFILE_MIN = 0,
|
||||||
|
H264PROFILE_BASELINE = H264PROFILE_MIN,
|
||||||
|
H264PROFILE_MAIN = 1,
|
||||||
|
H264PROFILE_EXTENDED = 2,
|
||||||
|
H264PROFILE_HIGH = 3,
|
||||||
|
H264PROFILE_HIGH10PROFILE = 4,
|
||||||
|
H264PROFILE_HIGH422PROFILE = 5,
|
||||||
|
H264PROFILE_HIGH444PREDICTIVEPROFILE = 6,
|
||||||
|
H264PROFILE_SCALABLEBASELINE = 7,
|
||||||
|
H264PROFILE_SCALABLEHIGH = 8,
|
||||||
|
H264PROFILE_STEREOHIGH = 9,
|
||||||
|
H264PROFILE_MULTIVIEWHIGH = 10,
|
||||||
|
H264PROFILE_MAX = H264PROFILE_MULTIVIEWHIGH,
|
||||||
|
VP8PROFILE_MIN = 11,
|
||||||
|
VP8PROFILE_ANY = VP8PROFILE_MIN,
|
||||||
|
VP8PROFILE_MAX = VP8PROFILE_ANY,
|
||||||
|
VP9PROFILE_MIN = 12,
|
||||||
|
VP9PROFILE_PROFILE0 = VP9PROFILE_MIN,
|
||||||
|
VP9PROFILE_PROFILE1 = 13,
|
||||||
|
VP9PROFILE_PROFILE2 = 14,
|
||||||
|
VP9PROFILE_PROFILE3 = 15,
|
||||||
|
VP9PROFILE_MAX = VP9PROFILE_PROFILE3,
|
||||||
|
HEVCPROFILE_MIN = 16,
|
||||||
|
HEVCPROFILE_MAIN = HEVCPROFILE_MIN,
|
||||||
|
HEVCPROFILE_MAIN10 = 17,
|
||||||
|
HEVCPROFILE_MAIN_STILL_PICTURE = 18,
|
||||||
|
HEVCPROFILE_MAX = HEVCPROFILE_MAIN_STILL_PICTURE,
|
||||||
|
DOLBYVISION_PROFILE0 = 19,
|
||||||
|
// Deprecated: DOLBYVISION_PROFILE4 = 20,
|
||||||
|
DOLBYVISION_PROFILE5 = 21,
|
||||||
|
DOLBYVISION_PROFILE7 = 22,
|
||||||
|
THEORAPROFILE_MIN = 23,
|
||||||
|
THEORAPROFILE_ANY = THEORAPROFILE_MIN,
|
||||||
|
THEORAPROFILE_MAX = THEORAPROFILE_ANY,
|
||||||
|
AV1PROFILE_MIN = 24,
|
||||||
|
AV1PROFILE_PROFILE_MAIN = AV1PROFILE_MIN,
|
||||||
|
AV1PROFILE_PROFILE_HIGH = 25,
|
||||||
|
AV1PROFILE_PROFILE_PRO = 26,
|
||||||
|
AV1PROFILE_MAX = AV1PROFILE_PROFILE_PRO,
|
||||||
|
DOLBYVISION_PROFILE8 = 27,
|
||||||
|
DOLBYVISION_PROFILE9 = 28,
|
||||||
|
HEVCPROFILE_EXT_MIN = 29,
|
||||||
|
HEVCPROFILE_REXT = HEVCPROFILE_EXT_MIN,
|
||||||
|
HEVCPROFILE_HIGH_THROUGHPUT = 30,
|
||||||
|
HEVCPROFILE_MULTIVIEW_MAIN = 31,
|
||||||
|
HEVCPROFILE_SCALABLE_MAIN = 32,
|
||||||
|
HEVCPROFILE_3D_MAIN = 33,
|
||||||
|
HEVCPROFILE_SCREEN_EXTENDED = 34,
|
||||||
|
HEVCPROFILE_SCALABLE_REXT = 35,
|
||||||
|
HEVCPROFILE_HIGH_THROUGHPUT_SCREEN_EXTENDED = 36,
|
||||||
|
HEVCPROFILE_EXT_MAX = HEVCPROFILE_HIGH_THROUGHPUT_SCREEN_EXTENDED,
|
||||||
|
VVCPROFILE_MIN = 37,
|
||||||
|
VVCPROFILE_MAIN10 = VVCPROFILE_MIN,
|
||||||
|
VVCPROFILE_MAIN12 = 38,
|
||||||
|
VVCPROFILE_MAIN12_INTRA = 39,
|
||||||
|
VVCPROIFLE_MULTILAYER_MAIN10 = 40,
|
||||||
|
VVCPROFILE_MAIN10_444 = 41,
|
||||||
|
VVCPROFILE_MAIN12_444 = 42,
|
||||||
|
VVCPROFILE_MAIN16_444 = 43,
|
||||||
|
VVCPROFILE_MAIN12_444_INTRA = 44,
|
||||||
|
VVCPROFILE_MAIN16_444_INTRA = 45,
|
||||||
|
VVCPROFILE_MULTILAYER_MAIN10_444 = 46,
|
||||||
|
VVCPROFILE_MAIN10_STILL_PICTURE = 47,
|
||||||
|
VVCPROFILE_MAIN12_STILL_PICTURE = 48,
|
||||||
|
VVCPROFILE_MAIN10_444_STILL_PICTURE = 49,
|
||||||
|
VVCPROFILE_MAIN12_444_STILL_PICTURE = 50,
|
||||||
|
VVCPROFILE_MAIN16_444_STILL_PICTURE = 51,
|
||||||
|
VVCPROFILE_MAX = VVCPROFILE_MAIN16_444_STILL_PICTURE,
|
||||||
|
VIDEO_CODEC_PROFILE_MAX = VVCPROFILE_MAIN16_444_STILL_PICTURE,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type VideoCodecLevel = number; // uint32
|
||||||
|
export const NoVideoCodecLevel: VideoCodecLevel = 0;
|
||||||
|
|
||||||
|
export type VideoCodecProfileLevel = {
|
||||||
|
codec: VideoCodec;
|
||||||
|
profile: VideoCodecProfile;
|
||||||
|
level: VideoCodecLevel;
|
||||||
|
};
|
113
apps/playground/src/media/mkv/codecs/aac.ts
Normal file
113
apps/playground/src/media/mkv/codecs/aac.ts
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
import { ParseCodecPrivateError } from '@/media/base/errors';
|
||||||
|
import { ArkErrors, type } from 'arktype';
|
||||||
|
|
||||||
|
export const AAC_CODEC_TYPE = 'AAC';
|
||||||
|
|
||||||
|
export const AudioObjectTypeSchema = type('1 | 2 | 3 | 4 | 5 | 29 | 67');
|
||||||
|
|
||||||
|
export const SamplingFrequencyIndexSchema = type('1|2|3|4|5|6|7|8|9|10|11|12');
|
||||||
|
|
||||||
|
export const ChannelConfigurationSchema = type('1 | 2 | 3 | 4 | 5 | 6 | 7');
|
||||||
|
|
||||||
|
export const AudioSpecificConfigSchema = type({
|
||||||
|
audioObjectType: AudioObjectTypeSchema, // AAC profiles: Main, LC, SSR, LTP, HE, HE v2
|
||||||
|
samplingFrequencyIndex: SamplingFrequencyIndexSchema.optional(), // Sampling rate index
|
||||||
|
channelConfiguration: ChannelConfigurationSchema, // Channel config (1-7)
|
||||||
|
sbrPresent: type.boolean.optional(), // Optional: Indicates SBR presence
|
||||||
|
psPresent: type.boolean.optional(), // Optional: Indicates PS presence (for HE-AAC v2)
|
||||||
|
});
|
||||||
|
|
||||||
|
export type AudioSpecificConfigType = typeof AudioSpecificConfigSchema.infer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse AudioSpecificConfig from codec_private Uint8Array
|
||||||
|
* @param codecPrivate - Uint8Array containing codec_private data
|
||||||
|
* @returns Parsed AudioSpecificConfig or throws an error if invalid
|
||||||
|
*/
|
||||||
|
export function parseAudioSpecificConfig(
|
||||||
|
codecPrivate: Uint8Array
|
||||||
|
): AudioSpecificConfigType {
|
||||||
|
if (codecPrivate.length < 2) {
|
||||||
|
throw new ParseCodecPrivateError(
|
||||||
|
AAC_CODEC_TYPE,
|
||||||
|
'codec_private data too short'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a DataView for bit-level manipulation
|
||||||
|
const view = new DataView(
|
||||||
|
codecPrivate.buffer,
|
||||||
|
codecPrivate.byteOffset,
|
||||||
|
codecPrivate.byteLength
|
||||||
|
);
|
||||||
|
let byteOffset = 0;
|
||||||
|
let bitOffset = 0;
|
||||||
|
|
||||||
|
// Helper function to read specific number of bits
|
||||||
|
function readBits(bits: number): number {
|
||||||
|
let value = 0;
|
||||||
|
for (let i = 0; i < bits; i++) {
|
||||||
|
const byte = view.getUint8(byteOffset);
|
||||||
|
const bit = (byte >> (7 - bitOffset)) & 1;
|
||||||
|
value = (value << 1) | bit;
|
||||||
|
bitOffset++;
|
||||||
|
if (bitOffset === 8) {
|
||||||
|
bitOffset = 0;
|
||||||
|
byteOffset++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read 5 bits for audioObjectType
|
||||||
|
const audioObjectType = readBits(5);
|
||||||
|
|
||||||
|
// Read 4 bits for samplingFrequencyIndex
|
||||||
|
const samplingFrequencyIndex = readBits(4);
|
||||||
|
|
||||||
|
// Read 4 bits for channelConfiguration
|
||||||
|
const channelConfiguration = readBits(4);
|
||||||
|
|
||||||
|
// Check for SBR/PS extension (if audioObjectType indicates HE-AAC)
|
||||||
|
let sbrPresent = false;
|
||||||
|
let psPresent = false;
|
||||||
|
if (audioObjectType === 5 || audioObjectType === 29) {
|
||||||
|
sbrPresent = true;
|
||||||
|
if (audioObjectType === 29) {
|
||||||
|
psPresent = true; // HE-AAC v2 includes Parametric Stereo
|
||||||
|
}
|
||||||
|
// Skip extension-specific bits if present (simplified here)
|
||||||
|
// In real cases, additional parsing may be needed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the result object
|
||||||
|
const config: AudioSpecificConfigType = {
|
||||||
|
audioObjectType:
|
||||||
|
audioObjectType as AudioSpecificConfigType['audioObjectType'],
|
||||||
|
samplingFrequencyIndex:
|
||||||
|
samplingFrequencyIndex as AudioSpecificConfigType['samplingFrequencyIndex'],
|
||||||
|
channelConfiguration:
|
||||||
|
channelConfiguration as AudioSpecificConfigType['channelConfiguration'],
|
||||||
|
...(sbrPresent && { sbrPresent }),
|
||||||
|
...(psPresent && { psPresent }),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Validate with arktype
|
||||||
|
const validation = AudioSpecificConfigSchema(config);
|
||||||
|
if (validation instanceof ArkErrors) {
|
||||||
|
const error = new ParseCodecPrivateError(
|
||||||
|
AAC_CODEC_TYPE,
|
||||||
|
'Invalid AudioSpecificConfig'
|
||||||
|
);
|
||||||
|
error.cause = validation;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function genCodecIdByAudioSpecificConfig(
|
||||||
|
config: AudioSpecificConfigType
|
||||||
|
) {
|
||||||
|
return `mp4a.40.${config.audioObjectType}`;
|
||||||
|
}
|
125
apps/playground/src/media/mkv/codecs/avc.ts
Normal file
125
apps/playground/src/media/mkv/codecs/avc.ts
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
import { ParseCodecPrivateError } from '@/media/base/errors';
|
||||||
|
import { type } from 'arktype';
|
||||||
|
|
||||||
|
export const AVC_CODEC_TYPE = 'h264(AVC)';
|
||||||
|
|
||||||
|
export const AVCDecoderConfigurationRecordSchema = type({
|
||||||
|
configurationVersion: type.number, // Configuration version, typically 1
|
||||||
|
avcProfileIndication: type.number, // AVC profile
|
||||||
|
profileCompatibility: type.number, // Profile compatibility
|
||||||
|
avcLevelIndication: type.number, // AVC level
|
||||||
|
lengthSizeMinusOne: type.number, // NAL unit length field size minus 1
|
||||||
|
sps: type
|
||||||
|
.instanceOf(Uint8Array<ArrayBufferLike>)
|
||||||
|
.array()
|
||||||
|
.atLeastLength(1), // Sequence Parameter Sets (SPS)
|
||||||
|
pps: type
|
||||||
|
.instanceOf(Uint8Array<ArrayBufferLike>)
|
||||||
|
.array()
|
||||||
|
.atLeastLength(1), // Picture Parameter Sets (PPS)
|
||||||
|
});
|
||||||
|
|
||||||
|
export type AVCDecoderConfigurationRecordType =
|
||||||
|
typeof AVCDecoderConfigurationRecordSchema.infer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse AVCDecoderConfigurationRecord from codec_private Uint8Array
|
||||||
|
* @param codecPrivate - Uint8Array containing codec_private data
|
||||||
|
* @returns Parsed AVCDecoderConfigurationRecord or throws an error if invalid
|
||||||
|
*/
|
||||||
|
export function parseAVCDecoderConfigurationRecord(
|
||||||
|
codecPrivate: Uint8Array
|
||||||
|
): AVCDecoderConfigurationRecordType {
|
||||||
|
let offset = 0;
|
||||||
|
|
||||||
|
// Check if data length is sufficient
|
||||||
|
if (codecPrivate.length < 5) {
|
||||||
|
throw new ParseCodecPrivateError(
|
||||||
|
AVC_CODEC_TYPE,
|
||||||
|
'Input data too short for AVCDecoderConfigurationRecord'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const configurationVersion = codecPrivate[offset++];
|
||||||
|
const avcProfileIndication = codecPrivate[offset++];
|
||||||
|
const profileCompatibility = codecPrivate[offset++];
|
||||||
|
const avcLevelIndication = codecPrivate[offset++];
|
||||||
|
|
||||||
|
// Read lengthSizeMinusOne (first 6 bits are reserved, typically 0xFF, last 2 bits are the value)
|
||||||
|
const lengthSizeMinusOne = codecPrivate[offset++] & 0x03;
|
||||||
|
|
||||||
|
// Read number of SPS (first 3 bits are reserved, typically 0xE0, last 5 bits are SPS count)
|
||||||
|
const numOfSPS = codecPrivate[offset++] & 0x1f;
|
||||||
|
const sps: Uint8Array[] = [];
|
||||||
|
|
||||||
|
// Parse SPS
|
||||||
|
for (let i = 0; i < numOfSPS; i++) {
|
||||||
|
if (offset + 2 > codecPrivate.length) {
|
||||||
|
throw new ParseCodecPrivateError(AVC_CODEC_TYPE, 'Invalid SPS length');
|
||||||
|
}
|
||||||
|
|
||||||
|
const spsLength = (codecPrivate[offset] << 8) | codecPrivate[offset + 1];
|
||||||
|
offset += 2;
|
||||||
|
|
||||||
|
if (offset + spsLength > codecPrivate.length) {
|
||||||
|
throw new ParseCodecPrivateError(
|
||||||
|
AVC_CODEC_TYPE,
|
||||||
|
'SPS data exceeds buffer length'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
sps.push(codecPrivate.subarray(offset, offset + spsLength));
|
||||||
|
offset += spsLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read number of PPS
|
||||||
|
if (offset >= codecPrivate.length) {
|
||||||
|
throw new ParseCodecPrivateError(AVC_CODEC_TYPE, 'No space for PPS count');
|
||||||
|
}
|
||||||
|
const numOfPPS = codecPrivate[offset++];
|
||||||
|
const pps: Uint8Array[] = [];
|
||||||
|
|
||||||
|
// Parse PPS
|
||||||
|
for (let i = 0; i < numOfPPS; i++) {
|
||||||
|
if (offset + 2 > codecPrivate.length) {
|
||||||
|
throw new ParseCodecPrivateError(AVC_CODEC_TYPE, 'Invalid PPS length');
|
||||||
|
}
|
||||||
|
|
||||||
|
const ppsLength = (codecPrivate[offset] << 8) | codecPrivate[offset + 1];
|
||||||
|
offset += 2;
|
||||||
|
|
||||||
|
if (offset + ppsLength > codecPrivate.length) {
|
||||||
|
throw new ParseCodecPrivateError(
|
||||||
|
AVC_CODEC_TYPE,
|
||||||
|
'PPS data exceeds buffer length'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pps.push(codecPrivate.subarray(offset, offset + ppsLength));
|
||||||
|
offset += ppsLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
configurationVersion,
|
||||||
|
avcProfileIndication,
|
||||||
|
profileCompatibility,
|
||||||
|
avcLevelIndication,
|
||||||
|
lengthSizeMinusOne,
|
||||||
|
sps,
|
||||||
|
pps,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function genCodecIdByAVCDecoderConfigurationRecord(
|
||||||
|
config: AVCDecoderConfigurationRecordType
|
||||||
|
): string {
|
||||||
|
const profileHex = config.avcProfileIndication.toString(16).padStart(2, '0');
|
||||||
|
const profileCompatHex = config.profileCompatibility
|
||||||
|
.toString(16)
|
||||||
|
.padStart(2, '0');
|
||||||
|
const levelHex = (config.avcLevelIndication / 10)
|
||||||
|
.toString(16)
|
||||||
|
.replace(/./g, '')
|
||||||
|
.padStart(2, '0');
|
||||||
|
return `avc1.${profileHex}${profileCompatHex}${levelHex}`;
|
||||||
|
}
|
144
apps/playground/src/media/mkv/codecs/hevc.ts
Normal file
144
apps/playground/src/media/mkv/codecs/hevc.ts
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
import { ParseCodecPrivateError } from '@/media/base/errors';
|
||||||
|
import { ArkErrors, type } from 'arktype';
|
||||||
|
|
||||||
|
export const HEVC_CODEC_TYPE = 'h265(HEVC)';
|
||||||
|
|
||||||
|
export const HEVCDecoderConfigurationRecordArraySchema = type({
|
||||||
|
arrayCompleteness: type.boolean,
|
||||||
|
reserved: type.number,
|
||||||
|
NALUnitType: type.number,
|
||||||
|
numNalus: type.number,
|
||||||
|
nalUnits: type.instanceOf(Uint8Array<ArrayBufferLike>).array(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type HEVCDecoderConfigurationRecordArrayType =
|
||||||
|
typeof HEVCDecoderConfigurationRecordArraySchema.infer;
|
||||||
|
|
||||||
|
// Define the schema for HEVCDecoderConfigurationRecord
|
||||||
|
export const HEVCDecoderConfigurationRecordSchema = type({
|
||||||
|
configurationVersion: type.number, // Must be 1
|
||||||
|
generalProfileSpace: type.number,
|
||||||
|
generalTierFlag: type.boolean,
|
||||||
|
generalProfileIdc: type.number,
|
||||||
|
generalProfileCompatibilityFlags: type.number,
|
||||||
|
generalConstraintIndicatorFlags: type.number.array().exactlyLength(6), // Fixed 6-byte array
|
||||||
|
generalLevelIdc: type.number,
|
||||||
|
reserved1: type.number, // 4 bits reserved, must be 1111
|
||||||
|
minSpatialSegmentationIdc: type.number,
|
||||||
|
reserved2: type.number, // 6 bits reserved, must be 111111
|
||||||
|
parallelismType: type.number,
|
||||||
|
chromaFormat: type.number,
|
||||||
|
bitDepthLumaMinus8: type.number,
|
||||||
|
bitDepthChromaMinus8: type.number,
|
||||||
|
avgFrameRate: type.number,
|
||||||
|
constantFrameRate: type.number,
|
||||||
|
numTemporalLayers: type.number,
|
||||||
|
temporalIdNested: type.boolean,
|
||||||
|
lengthSizeMinusOne: type.number,
|
||||||
|
numOfArrays: type.number,
|
||||||
|
arrays: HEVCDecoderConfigurationRecordArraySchema.array(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type HEVCDecoderConfigurationRecordType =
|
||||||
|
typeof HEVCDecoderConfigurationRecordSchema.infer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse HEVCDecoderConfigurationRecord from codec_private Uint8Array
|
||||||
|
* @param codecPrivate - Uint8Array containing codec_private data
|
||||||
|
* @returns Parsed HEVCDecoderConfigurationRecord or throws an error if invalid
|
||||||
|
*/
|
||||||
|
export function parseHEVCDecoderConfigurationRecord(
|
||||||
|
codecPrivate: Uint8Array
|
||||||
|
): HEVCDecoderConfigurationRecordType {
|
||||||
|
let offset = 0;
|
||||||
|
|
||||||
|
// Read and validate basic fields
|
||||||
|
const config: HEVCDecoderConfigurationRecordType = {
|
||||||
|
configurationVersion: codecPrivate[offset++],
|
||||||
|
generalProfileSpace: codecPrivate[offset] >> 6,
|
||||||
|
generalTierFlag: Boolean(codecPrivate[offset] & 0x20),
|
||||||
|
generalProfileIdc: codecPrivate[offset++] & 0x1f,
|
||||||
|
generalProfileCompatibilityFlags:
|
||||||
|
(codecPrivate[offset] << 24) |
|
||||||
|
(codecPrivate[offset + 1] << 16) |
|
||||||
|
(codecPrivate[offset + 2] << 8) |
|
||||||
|
codecPrivate[offset + 3],
|
||||||
|
generalConstraintIndicatorFlags: Array.from(
|
||||||
|
codecPrivate.subarray(offset + 4, offset + 10)
|
||||||
|
),
|
||||||
|
generalLevelIdc: codecPrivate[offset + 10],
|
||||||
|
reserved1: (codecPrivate[offset + 11] & 0xf0) >> 4, // 4 bits
|
||||||
|
minSpatialSegmentationIdc:
|
||||||
|
((codecPrivate[offset + 11] & 0x0f) << 8) | codecPrivate[offset + 12],
|
||||||
|
reserved2: (codecPrivate[offset + 13] & 0xfc) >> 2, // 6 bits
|
||||||
|
parallelismType: codecPrivate[offset + 13] & 0x03,
|
||||||
|
chromaFormat: (codecPrivate[offset + 14] & 0xe0) >> 5,
|
||||||
|
bitDepthLumaMinus8: (codecPrivate[offset + 14] & 0x1c) >> 2,
|
||||||
|
bitDepthChromaMinus8: codecPrivate[offset + 14] & 0x03,
|
||||||
|
avgFrameRate: (codecPrivate[offset + 15] << 8) | codecPrivate[offset + 16],
|
||||||
|
constantFrameRate: (codecPrivate[offset + 17] & 0xc0) >> 6,
|
||||||
|
numTemporalLayers: (codecPrivate[offset + 17] & 0x38) >> 3,
|
||||||
|
temporalIdNested: Boolean(codecPrivate[offset + 17] & 0x04),
|
||||||
|
lengthSizeMinusOne: codecPrivate[offset + 17] & 0x03,
|
||||||
|
numOfArrays: codecPrivate[offset + 18],
|
||||||
|
arrays: [],
|
||||||
|
};
|
||||||
|
offset += 19;
|
||||||
|
|
||||||
|
// Parse NAL unit arrays
|
||||||
|
const arrays = config.arrays;
|
||||||
|
for (let i = 0; i < config.numOfArrays; i++) {
|
||||||
|
const array: HEVCDecoderConfigurationRecordArrayType = {
|
||||||
|
arrayCompleteness: Boolean(codecPrivate[offset] & 0x80),
|
||||||
|
reserved: (codecPrivate[offset] & 0x40) >> 6,
|
||||||
|
NALUnitType: codecPrivate[offset] & 0x3f,
|
||||||
|
numNalus: (codecPrivate[offset + 1] << 8) | codecPrivate[offset + 2],
|
||||||
|
nalUnits: [] as Uint8Array<ArrayBufferLike>[],
|
||||||
|
};
|
||||||
|
offset += 3;
|
||||||
|
|
||||||
|
for (let j = 0; j < array.numNalus; j++) {
|
||||||
|
const nalUnitLength =
|
||||||
|
(codecPrivate[offset] << 8) | codecPrivate[offset + 1];
|
||||||
|
offset += 2;
|
||||||
|
array.nalUnits.push(
|
||||||
|
codecPrivate.subarray(offset, offset + nalUnitLength)
|
||||||
|
);
|
||||||
|
offset += nalUnitLength;
|
||||||
|
}
|
||||||
|
arrays.push(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = { ...config, arrays };
|
||||||
|
|
||||||
|
// Validate using arktype
|
||||||
|
const validation = HEVCDecoderConfigurationRecordSchema(result);
|
||||||
|
if (validation instanceof ArkErrors) {
|
||||||
|
const error = new ParseCodecPrivateError(
|
||||||
|
HEVC_CODEC_TYPE,
|
||||||
|
'Invalid HEVC configuration record'
|
||||||
|
);
|
||||||
|
error.cause = validation;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function genCodecStringByHEVCDecoderConfigurationRecord(
|
||||||
|
config: HEVCDecoderConfigurationRecordType
|
||||||
|
) {
|
||||||
|
const profileSpace =
|
||||||
|
config.generalProfileSpace === 0
|
||||||
|
? ''
|
||||||
|
: String.fromCharCode(65 + config.generalProfileSpace - 1);
|
||||||
|
const profileIdcHex = config.generalProfileIdc.toString(16);
|
||||||
|
const tier = config.generalTierFlag ? '7' : '6';
|
||||||
|
const levelMajor = Math.floor(config.generalLevelIdc / 30);
|
||||||
|
const levelMinor =
|
||||||
|
config.generalLevelIdc % 30 === 0 ? '0' : (config.generalLevelIdc % 30) / 3;
|
||||||
|
const levelStr = `L${config.generalLevelIdc.toString().padStart(3, '0')}`;
|
||||||
|
|
||||||
|
const constraint = '00';
|
||||||
|
return `hev1.${profileSpace}${profileIdcHex}.${tier}.${levelStr}.${constraint}`;
|
||||||
|
}
|
229
apps/playground/src/media/mkv/codecs/index.ts
Normal file
229
apps/playground/src/media/mkv/codecs/index.ts
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
import { AudioCodec } from '../../base/audio_codecs';
|
||||||
|
import { UnsupportCodecError } from '../../base/errors';
|
||||||
|
import { VideoCodec } from '../../base/video_codecs';
|
||||||
|
import type { TrackEntryType } from '../schema';
|
||||||
|
import {
|
||||||
|
genCodecIdByAudioSpecificConfig,
|
||||||
|
parseAudioSpecificConfig,
|
||||||
|
} from './aac';
|
||||||
|
import {
|
||||||
|
genCodecIdByAVCDecoderConfigurationRecord,
|
||||||
|
parseAVCDecoderConfigurationRecord,
|
||||||
|
} from './avc';
|
||||||
|
|
||||||
|
export const VideoCodecId = {
|
||||||
|
VCM: 'V_MS/VFW/FOURCC',
|
||||||
|
UNCOMPRESSED: 'V_UNCOMPRESSED',
|
||||||
|
MPEG4_ISO_SP: 'V_MPEG4/ISO/SP',
|
||||||
|
MPEG4_ISO_ASP: 'V_MPEG4/ISO/ASP',
|
||||||
|
MPEG4_ISO_AP: 'V_MPEG4/ISO/AP',
|
||||||
|
MPEG4_MS_V3: 'V_MPEG4/MS/V3',
|
||||||
|
MPEG1: 'V_MPEG1',
|
||||||
|
MPEG2: 'V_MPEG2',
|
||||||
|
H264: 'V_MPEG4/ISO/AVC',
|
||||||
|
HEVC: 'V_MPEGH/ISO/HEVC',
|
||||||
|
AVS2: 'V_AVS2',
|
||||||
|
AVS3: 'V_AVS3',
|
||||||
|
RV10: 'V_REAL/RV10',
|
||||||
|
RV20: 'V_REAL/RV20',
|
||||||
|
RV30: 'V_REAL/RV30',
|
||||||
|
RV40: 'V_REAL/RV40',
|
||||||
|
QUICKTIME: 'V_QUICKTIME',
|
||||||
|
THEORA: 'V_THEORA',
|
||||||
|
PROPRES: 'V_PRORES',
|
||||||
|
VP8: 'V_VP8',
|
||||||
|
VP9: 'V_VP9',
|
||||||
|
FFV1: 'V_FFV1',
|
||||||
|
AV1: 'V_AV1',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type VideoCodecIdType =
|
||||||
|
| `${(typeof VideoCodecId)[keyof typeof VideoCodecId]}`
|
||||||
|
| string;
|
||||||
|
|
||||||
|
export const AudioCodecId = {
|
||||||
|
MPEG_L3: 'A_MPEG/L3',
|
||||||
|
MPEG_L2: 'A_MPEG/L2',
|
||||||
|
MPEG_L1: 'A_MPEG/L1',
|
||||||
|
PCM_INT_BIG: 'A_PCM/INT/BIG',
|
||||||
|
PCM_INT_LIT: 'A_PCM/INT/LIT',
|
||||||
|
PCM_FLOAT_IEEE: 'A_PCM/FLOAT/IEEE',
|
||||||
|
MPC: 'A_MPC',
|
||||||
|
AC3: 'A_AC3',
|
||||||
|
AC3_BSID9: 'A_AC3/BSID9',
|
||||||
|
AC3_BSID10: 'A_AC3/BSID10',
|
||||||
|
ALAC: 'A_ALAC',
|
||||||
|
DTS: 'A_DTS',
|
||||||
|
DTS_EXPRESS: 'A_DTS/EXPRESS',
|
||||||
|
DTS_LOSSLESS: 'A_DTS/LOSSLESS',
|
||||||
|
VORBIS: 'A_VORBIS',
|
||||||
|
OPUS: 'A_OPUS',
|
||||||
|
FLAC: 'A_FLAC',
|
||||||
|
EAC3: 'A_EAC3',
|
||||||
|
REAL_14_4: 'A_REAL/14_4',
|
||||||
|
REAL_28_8: 'A_REAL/28_8',
|
||||||
|
REAL_COOK: 'A_REAL/COOK',
|
||||||
|
REAL_SIPR: 'A_REAL/SIPR',
|
||||||
|
REAL_RALF: 'A_REAL/RALF',
|
||||||
|
REAL_ATRC: 'A_REAL/ATRC',
|
||||||
|
MS_ACM: 'A_MS/ACM',
|
||||||
|
AAC: 'A_AAC',
|
||||||
|
AAC_MPEG2_MAIN: 'A_AAC/MPEG2/MAIN',
|
||||||
|
AAC_MPEG2_LC: 'A_AAC/MPEG2/LC',
|
||||||
|
AAC_MPEG2_LC_SBR: 'A_AAC/MPEG2/LC/SBR',
|
||||||
|
AAC_MPEG2_SSR: 'A_AAC/MPEG2/SSR',
|
||||||
|
AAC_MPEG4_MAIN: 'A_AAC/MPEG4/MAIN',
|
||||||
|
AAC_MPEG4_LC: 'A_AAC/MPEG4/LC',
|
||||||
|
AAC_MPEG4_SBR: 'A_AAC/MPEG4/LC/SBR',
|
||||||
|
AAC_MPEG4_SSR: 'A_AAC/MPEG4/SSR',
|
||||||
|
AAC_MPEG4_LTP: 'A_AAC/MPEG4/LTP',
|
||||||
|
QUICKTIME: 'A_QUICKTIME',
|
||||||
|
QDMC: 'A_QUICKTIME/QDMC',
|
||||||
|
QDM2: 'A_QUICKTIME/QDM2',
|
||||||
|
TTA1: 'A_TTA1',
|
||||||
|
WAVEPACK4: 'A_WAVPACK4',
|
||||||
|
ATRAC: 'A_ATRAC/AT1',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type AudioCodecIdType =
|
||||||
|
| `${(typeof AudioCodecId)[keyof typeof AudioCodecId]}`
|
||||||
|
| string;
|
||||||
|
|
||||||
|
export const SubtitleCodecId = {
|
||||||
|
UTF8: 'S_TEXT/UTF8',
|
||||||
|
SSA: 'S_TEXT/SSA',
|
||||||
|
ASS: 'S_TEXT/ASS',
|
||||||
|
WEBVTT: 'S_TEXT/WEBVTT',
|
||||||
|
BMP: 'S_IMAGE/BMP',
|
||||||
|
DVBSUB: 'S_DVBSUB',
|
||||||
|
VOBSUB: 'S_VOBSUB',
|
||||||
|
HDMV_PGS: 'S_HDMV/PGS',
|
||||||
|
HDMV_TEXTST: 'S_HDMV/TEXTST',
|
||||||
|
KATE: 'S_KATE',
|
||||||
|
ARIBSUB: 'S_ARIBSUB',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type SubtitleCodecIdType =
|
||||||
|
| `${(typeof SubtitleCodecId)[keyof typeof SubtitleCodecId]}`
|
||||||
|
| string;
|
||||||
|
|
||||||
|
export function videoCodecIdToWebCodecsVideoDecoder(
|
||||||
|
track: TrackEntryType
|
||||||
|
): [VideoCodec, string] {
|
||||||
|
const codecId = track.CodecID;
|
||||||
|
const codecPrivate = track.CodecPrivate;
|
||||||
|
switch (codecId) {
|
||||||
|
case VideoCodecId.HEVC:
|
||||||
|
return [VideoCodec.HEVC, 'hevc'];
|
||||||
|
case VideoCodecId.VP9:
|
||||||
|
return [VideoCodec.VP9, 'vp09'];
|
||||||
|
case VideoCodecId.AV1:
|
||||||
|
return [VideoCodec.AV1, 'av1'];
|
||||||
|
case VideoCodecId.H264:
|
||||||
|
if (!codecPrivate) {
|
||||||
|
throw new UnsupportCodecError(
|
||||||
|
'h264(without codec_private profile)',
|
||||||
|
'web codecs audio decoder'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
VideoCodec.H264,
|
||||||
|
genCodecIdByAVCDecoderConfigurationRecord(
|
||||||
|
parseAVCDecoderConfigurationRecord(codecPrivate)
|
||||||
|
),
|
||||||
|
];
|
||||||
|
case VideoCodecId.THEORA:
|
||||||
|
return [VideoCodec.Theora, 'theora'];
|
||||||
|
case VideoCodecId.VP8:
|
||||||
|
return [VideoCodec.VP8, 'vp8'];
|
||||||
|
case VideoCodecId.MPEG4_ISO_SP:
|
||||||
|
return [VideoCodec.MPEG4, 'mp4v.01.3'];
|
||||||
|
case VideoCodecId.MPEG4_ISO_ASP:
|
||||||
|
return [VideoCodec.MPEG4, 'mp4v.20.9'];
|
||||||
|
case VideoCodecId.MPEG4_ISO_AP:
|
||||||
|
return [VideoCodec.MPEG4, 'mp4v.20.9'];
|
||||||
|
default:
|
||||||
|
throw new UnsupportCodecError(codecId, 'web codecs video decoder');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function videoCodecIdToWebCodecsAudioDecoder(
|
||||||
|
track: TrackEntryType
|
||||||
|
): [AudioCodec, string] {
|
||||||
|
const codecId = track.CodecID;
|
||||||
|
const codecPrivate = track.CodecPrivate;
|
||||||
|
const bitDepth = track.Audio?.BitDepth;
|
||||||
|
switch (track.CodecID) {
|
||||||
|
case AudioCodecId.AAC_MPEG4_MAIN:
|
||||||
|
case AudioCodecId.AAC_MPEG2_MAIN:
|
||||||
|
return [AudioCodec.AAC, 'mp4a.40.1'];
|
||||||
|
case AudioCodecId.AAC_MPEG2_LC:
|
||||||
|
case AudioCodecId.AAC_MPEG4_LC:
|
||||||
|
return [AudioCodec.AAC, 'mp4a.40.2'];
|
||||||
|
case AudioCodecId.AAC_MPEG2_SSR:
|
||||||
|
case AudioCodecId.AAC_MPEG4_SSR:
|
||||||
|
return [AudioCodec.AAC, 'mp4a.40.3'];
|
||||||
|
case AudioCodecId.AAC_MPEG4_LTP:
|
||||||
|
return [AudioCodec.AAC, 'mp4a.40.4'];
|
||||||
|
case AudioCodecId.AAC_MPEG2_LC_SBR:
|
||||||
|
case AudioCodecId.AAC_MPEG4_SBR:
|
||||||
|
return [AudioCodec.AAC, 'mp4a.40.5'];
|
||||||
|
case AudioCodecId.AAC:
|
||||||
|
return [
|
||||||
|
AudioCodec.AAC,
|
||||||
|
codecPrivate
|
||||||
|
? genCodecIdByAudioSpecificConfig(
|
||||||
|
parseAudioSpecificConfig(codecPrivate)
|
||||||
|
)
|
||||||
|
: 'mp4a.40.2',
|
||||||
|
];
|
||||||
|
case AudioCodecId.AC3:
|
||||||
|
case AudioCodecId.AC3_BSID9:
|
||||||
|
return [AudioCodec.AC3, 'ac-3'];
|
||||||
|
case AudioCodecId.EAC3:
|
||||||
|
case AudioCodecId.AC3_BSID10:
|
||||||
|
return [AudioCodec.EAC3, 'ec-3'];
|
||||||
|
case AudioCodecId.MPEG_L3:
|
||||||
|
return [AudioCodec.MP3, 'mp3'];
|
||||||
|
case AudioCodecId.VORBIS:
|
||||||
|
return [AudioCodec.Vorbis, 'vorbis'];
|
||||||
|
case AudioCodecId.FLAC:
|
||||||
|
return [AudioCodec.FLAC, 'flac'];
|
||||||
|
case AudioCodecId.OPUS:
|
||||||
|
return [AudioCodec.Opus, 'opus'];
|
||||||
|
case AudioCodecId.ALAC:
|
||||||
|
return [AudioCodec.ALAC, 'alac'];
|
||||||
|
case AudioCodecId.PCM_INT_BIG:
|
||||||
|
if (bitDepth === 16) {
|
||||||
|
return [AudioCodec.PCM_S16BE, 'pcm-s16be'];
|
||||||
|
}
|
||||||
|
if (bitDepth === 24) {
|
||||||
|
return [AudioCodec.PCM_S24BE, 'pcm-s24be'];
|
||||||
|
}
|
||||||
|
if (bitDepth === 32) {
|
||||||
|
return [AudioCodec.PCM_S32BE, 'pcm-s32be'];
|
||||||
|
}
|
||||||
|
throw new UnsupportCodecError(
|
||||||
|
`${codecId}(${bitDepth}b)`,
|
||||||
|
'web codecs audio decoder'
|
||||||
|
);
|
||||||
|
case AudioCodecId.PCM_INT_LIT:
|
||||||
|
if (bitDepth === 16) {
|
||||||
|
return [AudioCodec.PCM_S16LE, 'pcm-s16le'];
|
||||||
|
}
|
||||||
|
if (bitDepth === 24) {
|
||||||
|
return [AudioCodec.PCM_S24LE, 'pcm-s24le'];
|
||||||
|
}
|
||||||
|
if (bitDepth === 32) {
|
||||||
|
return [AudioCodec.PCM_S32LE, 'pcm-s32le'];
|
||||||
|
}
|
||||||
|
throw new UnsupportCodecError(
|
||||||
|
`${codecId}(${bitDepth}b)`,
|
||||||
|
'web codecs audio decoder'
|
||||||
|
);
|
||||||
|
case AudioCodecId.PCM_FLOAT_IEEE:
|
||||||
|
return [AudioCodec.PCM_F32LE, 'pcm-f32le'];
|
||||||
|
default:
|
||||||
|
throw new UnsupportCodecError(codecId, 'web codecs audio decoder');
|
||||||
|
}
|
||||||
|
}
|
@ -8,8 +8,8 @@ import {
|
|||||||
type EbmlSegmentTagType,
|
type EbmlSegmentTagType,
|
||||||
EbmlTagIdEnum,
|
EbmlTagIdEnum,
|
||||||
EbmlTagPosition,
|
EbmlTagPosition,
|
||||||
EbmlTagsTagType,
|
type EbmlTagsTagType,
|
||||||
EbmlTagTagType,
|
type EbmlTagTagType,
|
||||||
type EbmlTagType,
|
type EbmlTagType,
|
||||||
type EbmlTrackEntryTagType,
|
type EbmlTrackEntryTagType,
|
||||||
type EbmlTracksTagType,
|
type EbmlTracksTagType,
|
||||||
@ -28,7 +28,7 @@ import {
|
|||||||
SeekHeadSchema,
|
SeekHeadSchema,
|
||||||
type SeekHeadType,
|
type SeekHeadType,
|
||||||
TagSchema,
|
TagSchema,
|
||||||
TagType,
|
type TagType,
|
||||||
TrackEntrySchema,
|
TrackEntrySchema,
|
||||||
type TrackEntryType,
|
type TrackEntryType,
|
||||||
} from './schema';
|
} from './schema';
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
"linter": {
|
"linter": {
|
||||||
"rules": {
|
"rules": {
|
||||||
"style": {
|
"style": {
|
||||||
|
"useSingleCaseStatement": "off",
|
||||||
"noParameterProperties": "off",
|
"noParameterProperties": "off",
|
||||||
"noNonNullAssertion": "off"
|
"noNonNullAssertion": "off"
|
||||||
},
|
},
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"arktype": "^2.1.10",
|
"arktype": "^2.1.10",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
|
"media-codecs": "^2.0.2",
|
||||||
"mnemonist": "^0.40.3",
|
"mnemonist": "^0.40.3",
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"type-fest": "^4.37.0"
|
"type-fest": "^4.37.0"
|
||||||
|
19
packages/codecs/package.json
Normal file
19
packages/codecs/package.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "konoplayer-codecs",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"build": "rsbuild build",
|
||||||
|
"dev": "rsbuild dev",
|
||||||
|
"preview": "rsbuild preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"konoebml": "0.1.2-rc.5",
|
||||||
|
"lit": "^3.2.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@rsbuild/core": "^1.2.14",
|
||||||
|
"typescript": "^5.8.2"
|
||||||
|
}
|
||||||
|
}
|
0
packages/codecs/src/index.ts
Normal file
0
packages/codecs/src/index.ts
Normal file
25
pnpm-lock.yaml
generated
25
pnpm-lock.yaml
generated
@ -17,6 +17,9 @@ importers:
|
|||||||
lodash-es:
|
lodash-es:
|
||||||
specifier: ^4.17.21
|
specifier: ^4.17.21
|
||||||
version: 4.17.21
|
version: 4.17.21
|
||||||
|
media-codecs:
|
||||||
|
specifier: ^2.0.2
|
||||||
|
version: 2.0.2
|
||||||
mnemonist:
|
mnemonist:
|
||||||
specifier: ^0.40.3
|
specifier: ^0.40.3
|
||||||
version: 0.40.3
|
version: 0.40.3
|
||||||
@ -108,6 +111,22 @@ importers:
|
|||||||
specifier: ^2.9.93
|
specifier: ^2.9.93
|
||||||
version: 2.9.94
|
version: 2.9.94
|
||||||
|
|
||||||
|
packages/codecs:
|
||||||
|
dependencies:
|
||||||
|
konoebml:
|
||||||
|
specifier: 0.1.2-rc.5
|
||||||
|
version: 0.1.2-rc.5(arktype@2.1.10)
|
||||||
|
lit:
|
||||||
|
specifier: ^3.2.1
|
||||||
|
version: 3.2.1
|
||||||
|
devDependencies:
|
||||||
|
'@rsbuild/core':
|
||||||
|
specifier: ^1.2.14
|
||||||
|
version: 1.2.15
|
||||||
|
typescript:
|
||||||
|
specifier: ^5.8.2
|
||||||
|
version: 5.8.2
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
'@angular-devkit/core@19.1.8':
|
'@angular-devkit/core@19.1.8':
|
||||||
@ -1923,6 +1942,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
media-codecs@2.0.2:
|
||||||
|
resolution: {integrity: sha512-D7ygdW7j5yqkDJ9kX5H4UU2iC/fsreU2Vy49GxbN6OUeYso6U2QG6QgcwSn75eFUoM6ttVuTLAOt4qxQvLWdFw==}
|
||||||
|
engines: {node: '>=22.0.0', npm: '>=10.5.1', snowdev: '>=2.2.x'}
|
||||||
|
|
||||||
media-typer@0.3.0:
|
media-typer@0.3.0:
|
||||||
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
|
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
@ -4550,6 +4573,8 @@ snapshots:
|
|||||||
|
|
||||||
math-intrinsics@1.1.0: {}
|
math-intrinsics@1.1.0: {}
|
||||||
|
|
||||||
|
media-codecs@2.0.2: {}
|
||||||
|
|
||||||
media-typer@0.3.0: {}
|
media-typer@0.3.0: {}
|
||||||
|
|
||||||
media-typer@1.1.0: {}
|
media-typer@1.1.0: {}
|
||||||
|
Loading…
Reference in New Issue
Block a user