diff --git a/entries/all-boxes.ts b/entries/all-boxes.ts index c468da0b..802d5ece 100644 --- a/entries/all-boxes.ts +++ b/entries/all-boxes.ts @@ -52,6 +52,7 @@ export * from '#/boxes/kind'; export * from '#/boxes/leva'; export * from '#/boxes/lhvC'; export * from '#/boxes/lsel'; +export * from '#/boxes/lvcC'; export * from '#/boxes/maxr'; export * from '#/boxes/mdcv'; export * from '#/boxes/mdhd'; diff --git a/src/boxes/lvcC.ts b/src/boxes/lvcC.ts new file mode 100644 index 00000000..9330d4a7 --- /dev/null +++ b/src/boxes/lvcC.ts @@ -0,0 +1,80 @@ +import { Box } from '#/box'; +import { Log } from '#/log'; +import type { MultiBufferStream } from '#/buffer'; +import type { NaluArray } from '@types'; +import type { NALUArrays } from './displays/naluArrays'; + +export class lvcCBox extends Box { + static override readonly fourcc = 'lvcC' as const; + box_name = 'LCEVCConfigurationBox' as const; + + configurationVersion: number; + LCEVCProfileIndication: number; + LCEVCLevelIndication: number; + chroma_format_idc: number; + bit_depth_luma_minus8: number; + bit_depth_chroma_minus8: number; + lengthSizeMinusOne: number; + pic_width_in_luma_samples: number; + pic_height_in_luma_samples: number; + sc_in_stream: number; + gc_in_stream: number; + ai_in_stream: number; + nalu_arrays: NALUArrays; + + parse(stream: MultiBufferStream) { + this.configurationVersion = stream.readUint8(); + if (this.configurationVersion !== 1) { + Log.error( + 'BoxParser', + 'lvcC version ' + this.configurationVersion + ' not supported', + stream.isofile, + ); + return; + } + this.LCEVCProfileIndication = stream.readUint8(); + this.LCEVCLevelIndication = stream.readUint8(); + let tmp_byte = stream.readUint8(); + this.chroma_format_idc = (tmp_byte >> 6) & 0x3; + this.bit_depth_luma_minus8 = (tmp_byte >> 3) & 0x7; + this.bit_depth_chroma_minus8 = tmp_byte & 0x7; + tmp_byte = stream.readUint8(); + this.lengthSizeMinusOne = (tmp_byte >> 6) & 0x3; + let reserved = tmp_byte & 0x3f; + if (reserved !== 0x3f) { + Log.error('BoxParser', 'lvcC reserved parsing problem', stream.isofile); + return; + } + this.pic_width_in_luma_samples = stream.readUint32(); + this.pic_height_in_luma_samples = stream.readUint32(); + tmp_byte = stream.readUint8(); + this.sc_in_stream = (tmp_byte >> 7) & 0x1; + this.gc_in_stream = (tmp_byte >> 6) & 0x1; + this.ai_in_stream = (tmp_byte >> 5) & 0x1; + reserved = tmp_byte & 0x1f; + if (reserved !== 0x1f) { + Log.error('BoxParser', 'lvcC reserved parsing problem', stream.isofile); + return; + } + + this.nalu_arrays = []; + const numOfArrays = stream.readUint8(); + + for (let i = 0; i < numOfArrays; i++) { + const nalu_array = [] as NaluArray; + this.nalu_arrays.push(nalu_array); + tmp_byte = stream.readUint8(); + reserved = (tmp_byte >> 6) & 0x3; + if (reserved !== 0) { + Log.error('BoxParser', 'lvcC reserved parsing problem', stream.isofile); + return; + } + nalu_array.nalu_type = tmp_byte & 0x3f; + const numOfNalus = stream.readUint16(); + for (let j = 0; j < numOfNalus; j++) { + const length = stream.readUint16(); + nalu_array.push({ data: stream.readUint8Array(length) }); + } + } + } +} diff --git a/src/boxes/sampleentries/base.ts b/src/boxes/sampleentries/base.ts index 731a08fa..c020af87 100644 --- a/src/boxes/sampleentries/base.ts +++ b/src/boxes/sampleentries/base.ts @@ -4,6 +4,7 @@ import { Log } from '#/log'; import type { av1CBox } from '../av1C'; import type { avcCBox } from '../avcC'; import type { hvcCBox } from '../hvcC'; +import type { lvcCBox } from '../lvcC'; import type { vpcCBox } from '../vpcC'; import type { vvcCBox } from '../vvcC'; @@ -164,6 +165,7 @@ export class VisualSampleEntry extends SampleEntry { av1C?: av1CBox; avcC?: avcCBox; hvcC?: hvcCBox; + lvcC?: lvcCBox; vpcC?: vpcCBox; vvcC?: vvcCBox; diff --git a/src/boxes/sampleentries/sampleentry.ts b/src/boxes/sampleentries/sampleentry.ts index 123d7ecd..2d8c1bc8 100644 --- a/src/boxes/sampleentries/sampleentry.ts +++ b/src/boxes/sampleentries/sampleentry.ts @@ -3,6 +3,7 @@ import { avcCBox } from '#/boxes/avcC'; import { sinfBox } from '#/boxes/defaults'; import { esdsBox } from '#/boxes/esds'; import { hvcCBox } from '#/boxes/hvcC'; +import { lvcCBox } from '#/boxes/lvcC'; import { vpcCBox } from '#/boxes/vpcC'; import { vvcCBox } from '#/boxes/vvcC'; import { colrBox } from '#/boxes/colr'; @@ -203,6 +204,29 @@ export class lhv1SampleEntry extends VisualSampleEntry { box_name = 'LHEVCSampleEntry' as const; } +export class lvc1SampleEntry extends VisualSampleEntry { + lvcC: lvcCBox; + lvcCs: Array; + + static override readonly fourcc = 'lvc1' as const; + // ISO/IEC 14496-15:2024 13.4.1.2 + box_name = 'LCEVCSampleEntry' as const; + + /** @bundle box-codecs.js */ + getCodec(): string { + let baseCodec = super.getCodec(); + if (this.lvcC) { + baseCodec += '.'; + baseCodec += 'vprf'; + baseCodec += this.lvcC.LCEVCProfileIndication; + baseCodec += '.'; + baseCodec += 'vlev'; + baseCodec += this.lvcC.LCEVCLevelIndication; + } + return baseCodec; + } +} + export class dvh1SampleEntry extends VisualSampleEntry { static override readonly fourcc = 'dvh1' as const; }