502 lines
13 KiB
JavaScript
502 lines
13 KiB
JavaScript
//
|
|
// Created by Mingliang Chen on 17/12/21.
|
|
// illuspas[a]gmail.com
|
|
// Copyright (c) 2018 Nodemedia. All rights reserved.
|
|
//
|
|
|
|
const Bitop = require('./node_core_bitop');
|
|
const AAC_SAMPLE_RATE = [
|
|
96000, 88200, 64000, 48000,
|
|
44100, 32000, 24000, 22050,
|
|
16000, 12000, 11025, 8000,
|
|
7350, 0, 0, 0
|
|
];
|
|
|
|
const AAC_CHANNELS = [
|
|
0, 1, 2, 3, 4, 5, 6, 8
|
|
];
|
|
|
|
const AUDIO_CODEC_NAME = [
|
|
'',
|
|
'ADPCM',
|
|
'MP3',
|
|
'LinearLE',
|
|
'Nellymoser16',
|
|
'Nellymoser8',
|
|
'Nellymoser',
|
|
'G711A',
|
|
'G711U',
|
|
'',
|
|
'AAC',
|
|
'Speex',
|
|
'',
|
|
'',
|
|
'MP3-8K',
|
|
'DeviceSpecific',
|
|
'Uncompressed'
|
|
];
|
|
|
|
const AUDIO_SOUND_RATE = [
|
|
5512, 11025, 22050, 44100
|
|
];
|
|
|
|
const VIDEO_CODEC_NAME = [
|
|
'',
|
|
'Jpeg',
|
|
'Sorenson-H263',
|
|
'ScreenVideo',
|
|
'On2-VP6',
|
|
'On2-VP6-Alpha',
|
|
'ScreenVideo2',
|
|
'H264',
|
|
'',
|
|
'',
|
|
'',
|
|
'',
|
|
'H265'
|
|
];
|
|
|
|
function getObjectType(bitop) {
|
|
let audioObjectType = bitop.read(5);
|
|
if (audioObjectType === 31) {
|
|
audioObjectType = bitop.read(6) + 32;
|
|
}
|
|
return audioObjectType;
|
|
}
|
|
|
|
function getSampleRate(bitop, info) {
|
|
info.sampling_index = bitop.read(4);
|
|
return info.sampling_index == 0x0f ? bitop.read(24) : AAC_SAMPLE_RATE[info.sampling_index];
|
|
}
|
|
|
|
function readAACSpecificConfig(aacSequenceHeader) {
|
|
let info = {};
|
|
let bitop = new Bitop(aacSequenceHeader);
|
|
bitop.read(16);
|
|
info.object_type = getObjectType(bitop);
|
|
info.sample_rate = getSampleRate(bitop, info);
|
|
info.chan_config = bitop.read(4);
|
|
if (info.chan_config < AAC_CHANNELS.length) {
|
|
info.channels = AAC_CHANNELS[info.chan_config];
|
|
}
|
|
info.sbr = -1;
|
|
info.ps = -1;
|
|
if (info.object_type == 5 || info.object_type == 29) {
|
|
if (info.object_type == 29) {
|
|
info.ps = 1;
|
|
}
|
|
info.ext_object_type = 5;
|
|
info.sbr = 1;
|
|
info.sample_rate = getSampleRate(bitop, info);
|
|
info.object_type = getObjectType(bitop);
|
|
}
|
|
|
|
return info;
|
|
}
|
|
|
|
function getAACProfileName(info) {
|
|
switch (info.object_type) {
|
|
case 1:
|
|
return 'Main';
|
|
case 2:
|
|
if (info.ps > 0) {
|
|
return 'HEv2';
|
|
}
|
|
if (info.sbr > 0) {
|
|
return 'HE';
|
|
}
|
|
return 'LC';
|
|
case 3:
|
|
return 'SSR';
|
|
case 4:
|
|
return 'LTP';
|
|
case 5:
|
|
return 'SBR';
|
|
default:
|
|
return '';
|
|
}
|
|
}
|
|
|
|
function readH264SpecificConfig(avcSequenceHeader) {
|
|
let info = {};
|
|
let profile_idc, width, height, crop_left, crop_right,
|
|
crop_top, crop_bottom, frame_mbs_only, n, cf_idc,
|
|
num_ref_frames;
|
|
let bitop = new Bitop(avcSequenceHeader);
|
|
bitop.read(48);
|
|
info.width = 0;
|
|
info.height = 0;
|
|
|
|
do {
|
|
info.profile = bitop.read(8);
|
|
info.compat = bitop.read(8);
|
|
info.level = bitop.read(8);
|
|
info.nalu = (bitop.read(8) & 0x03) + 1;
|
|
info.nb_sps = bitop.read(8) & 0x1F;
|
|
if (info.nb_sps == 0) {
|
|
break;
|
|
}
|
|
/* nal size */
|
|
bitop.read(16);
|
|
|
|
/* nal type */
|
|
if (bitop.read(8) != 0x67) {
|
|
break;
|
|
}
|
|
/* SPS */
|
|
profile_idc = bitop.read(8);
|
|
|
|
/* flags */
|
|
bitop.read(8);
|
|
|
|
/* level idc */
|
|
bitop.read(8);
|
|
|
|
/* SPS id */
|
|
bitop.read_golomb();
|
|
|
|
if (profile_idc == 100 || profile_idc == 110 ||
|
|
profile_idc == 122 || profile_idc == 244 || profile_idc == 44 ||
|
|
profile_idc == 83 || profile_idc == 86 || profile_idc == 118) {
|
|
/* chroma format idc */
|
|
cf_idc = bitop.read_golomb();
|
|
|
|
if (cf_idc == 3) {
|
|
|
|
/* separate color plane */
|
|
bitop.read(1);
|
|
}
|
|
|
|
/* bit depth luma - 8 */
|
|
bitop.read_golomb();
|
|
|
|
/* bit depth chroma - 8 */
|
|
bitop.read_golomb();
|
|
|
|
/* qpprime y zero transform bypass */
|
|
bitop.read(1);
|
|
|
|
/* seq scaling matrix present */
|
|
if (bitop.read(1)) {
|
|
|
|
for (n = 0; n < (cf_idc != 3 ? 8 : 12); n++) {
|
|
|
|
/* seq scaling list present */
|
|
if (bitop.read(1)) {
|
|
|
|
/* TODO: scaling_list()
|
|
if (n < 6) {
|
|
} else {
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* log2 max frame num */
|
|
bitop.read_golomb();
|
|
|
|
/* pic order cnt type */
|
|
switch (bitop.read_golomb()) {
|
|
case 0:
|
|
|
|
/* max pic order cnt */
|
|
bitop.read_golomb();
|
|
break;
|
|
|
|
case 1:
|
|
|
|
/* delta pic order alwys zero */
|
|
bitop.read(1);
|
|
|
|
/* offset for non-ref pic */
|
|
bitop.read_golomb();
|
|
|
|
/* offset for top to bottom field */
|
|
bitop.read_golomb();
|
|
|
|
/* num ref frames in pic order */
|
|
num_ref_frames = bitop.read_golomb();
|
|
|
|
for (n = 0; n < num_ref_frames; n++) {
|
|
|
|
/* offset for ref frame */
|
|
bitop.read_golomb();
|
|
}
|
|
}
|
|
|
|
/* num ref frames */
|
|
info.avc_ref_frames = bitop.read_golomb();
|
|
|
|
/* gaps in frame num allowed */
|
|
bitop.read(1);
|
|
|
|
/* pic width in mbs - 1 */
|
|
width = bitop.read_golomb();
|
|
|
|
/* pic height in map units - 1 */
|
|
height = bitop.read_golomb();
|
|
|
|
/* frame mbs only flag */
|
|
frame_mbs_only = bitop.read(1);
|
|
|
|
if (!frame_mbs_only) {
|
|
|
|
/* mbs adaprive frame field */
|
|
bitop.read(1);
|
|
}
|
|
|
|
/* direct 8x8 inference flag */
|
|
bitop.read(1);
|
|
|
|
/* frame cropping */
|
|
if (bitop.read(1)) {
|
|
|
|
crop_left = bitop.read_golomb();
|
|
crop_right = bitop.read_golomb();
|
|
crop_top = bitop.read_golomb();
|
|
crop_bottom = bitop.read_golomb();
|
|
|
|
} else {
|
|
crop_left = 0;
|
|
crop_right = 0;
|
|
crop_top = 0;
|
|
crop_bottom = 0;
|
|
}
|
|
info.level = info.level / 10.0;
|
|
info.width = (width + 1) * 16 - (crop_left + crop_right) * 2;
|
|
info.height = (2 - frame_mbs_only) * (height + 1) * 16 - (crop_top + crop_bottom) * 2;
|
|
|
|
} while (0);
|
|
|
|
return info;
|
|
}
|
|
|
|
function HEVCParsePtl(bitop, hevc, max_sub_layers_minus1) {
|
|
let general_ptl = {};
|
|
|
|
general_ptl.profile_space = bitop.read(2);
|
|
general_ptl.tier_flag = bitop.read(1);
|
|
general_ptl.profile_idc = bitop.read(5);
|
|
general_ptl.profile_compatibility_flags = bitop.read(32);
|
|
general_ptl.general_progressive_source_flag = bitop.read(1);
|
|
general_ptl.general_interlaced_source_flag = bitop.read(1);
|
|
general_ptl.general_non_packed_constraint_flag = bitop.read(1);
|
|
general_ptl.general_frame_only_constraint_flag = bitop.read(1);
|
|
bitop.read(32);
|
|
bitop.read(12);
|
|
general_ptl.level_idc = bitop.read(8);
|
|
|
|
general_ptl.sub_layer_profile_present_flag = [];
|
|
general_ptl.sub_layer_level_present_flag = [];
|
|
|
|
for (let i = 0; i < max_sub_layers_minus1; i++) {
|
|
general_ptl.sub_layer_profile_present_flag[i] = bitop.read(1);
|
|
general_ptl.sub_layer_level_present_flag[i] = bitop.read(1);
|
|
}
|
|
|
|
if (max_sub_layers_minus1 > 0) {
|
|
for (let i = max_sub_layers_minus1; i < 8; i++) {
|
|
bitop.read(2)
|
|
}
|
|
}
|
|
|
|
general_ptl.sub_layer_profile_space = [];
|
|
general_ptl.sub_layer_tier_flag = [];
|
|
general_ptl.sub_layer_profile_idc = [];
|
|
general_ptl.sub_layer_profile_compatibility_flag = [];
|
|
general_ptl.sub_layer_progressive_source_flag = [];
|
|
general_ptl.sub_layer_interlaced_source_flag = [];
|
|
general_ptl.sub_layer_non_packed_constraint_flag = [];
|
|
general_ptl.sub_layer_frame_only_constraint_flag = [];
|
|
general_ptl.sub_layer_level_idc = [];
|
|
|
|
for (let i = 0; i < max_sub_layers_minus1; i++) {
|
|
if (general_ptl.sub_layer_profile_present_flag[i]) {
|
|
general_ptl.sub_layer_profile_space[i] = bitop.read(2);
|
|
general_ptl.sub_layer_tier_flag[i] = bitop.read(1);
|
|
general_ptl.sub_layer_profile_idc[i] = bitop.read(5);
|
|
general_ptl.sub_layer_profile_compatibility_flag[i] = bitop.read(32);
|
|
general_ptl.sub_layer_progressive_source_flag[i] = bitop.read(1);
|
|
general_ptl.sub_layer_interlaced_source_flag[i] = bitop.read(1);
|
|
general_ptl.sub_layer_non_packed_constraint_flag[i] = bitop.read(1);
|
|
general_ptl.sub_layer_frame_only_constraint_flag[i] = bitop.read(1);
|
|
bitop.read(32);
|
|
bitop.read(12);
|
|
}
|
|
if (general_ptl.sub_layer_level_present_flag[i]) {
|
|
general_ptl.sub_layer_level_idc[i] = bitop.read(8);
|
|
}
|
|
else {
|
|
general_ptl.sub_layer_level_idc[i] = 1;
|
|
}
|
|
}
|
|
return general_ptl;
|
|
}
|
|
|
|
function HEVCParseSPS(SPS, hevc) {
|
|
let psps = {};
|
|
let NumBytesInNALunit = SPS.length;
|
|
let NumBytesInRBSP = 0;
|
|
let rbsp_array = [];
|
|
let bitop = new Bitop(SPS);
|
|
|
|
bitop.read(1);//forbidden_zero_bit
|
|
bitop.read(6);//nal_unit_type
|
|
bitop.read(6);//nuh_reserved_zero_6bits
|
|
bitop.read(3);//nuh_temporal_id_plus1
|
|
|
|
for (let i = 2; i < NumBytesInNALunit; i++) {
|
|
if (i + 2 < NumBytesInNALunit && bitop.look(24) == 0x000003) {
|
|
rbsp_array.push(bitop.read(8));
|
|
rbsp_array.push(bitop.read(8));
|
|
i += 2;
|
|
let emulation_prevention_three_byte = bitop.read(8); /* equal to 0x03 */
|
|
} else {
|
|
rbsp_array.push(bitop.read(8));
|
|
}
|
|
}
|
|
let rbsp = Buffer.from(rbsp_array);
|
|
let rbspBitop = new Bitop(rbsp);
|
|
psps.sps_video_parameter_set_id = rbspBitop.read(4);
|
|
psps.sps_max_sub_layers_minus1 = rbspBitop.read(3);
|
|
psps.sps_temporal_id_nesting_flag = rbspBitop.read(1);
|
|
psps.profile_tier_level = HEVCParsePtl(rbspBitop, hevc, psps.sps_max_sub_layers_minus1);
|
|
psps.sps_seq_parameter_set_id = rbspBitop.read_golomb();
|
|
psps.chroma_format_idc = rbspBitop.read_golomb();
|
|
if (psps.chroma_format_idc == 3) {
|
|
psps.separate_colour_plane_flag = rbspBitop.read(1);
|
|
} else {
|
|
psps.separate_colour_plane_flag = 0;
|
|
}
|
|
psps.pic_width_in_luma_samples = rbspBitop.read_golomb();
|
|
psps.pic_height_in_luma_samples = rbspBitop.read_golomb();
|
|
psps.conformance_window_flag = rbspBitop.read(1);
|
|
if (psps.conformance_window_flag) {
|
|
let vert_mult = 1 + (psps.chroma_format_idc < 2);
|
|
let horiz_mult = 1 + (psps.chroma_format_idc < 3);
|
|
psps.conf_win_left_offset = rbspBitop.read_golomb() * horiz_mult;
|
|
psps.conf_win_right_offset = rbspBitop.read_golomb() * horiz_mult;
|
|
psps.conf_win_top_offset = rbspBitop.read_golomb() * vert_mult;
|
|
psps.conf_win_bottom_offset = rbspBitop.read_golomb() * vert_mult;
|
|
}
|
|
// Logger.debug(psps);
|
|
return psps;
|
|
}
|
|
|
|
function readHEVCSpecificConfig(hevcSequenceHeader) {
|
|
let info = {};
|
|
info.width = 0;
|
|
info.height = 0;
|
|
info.profile = 0;
|
|
info.level = 0;
|
|
// let bitop = new Bitop(hevcSequenceHeader);
|
|
// bitop.read(48);
|
|
hevcSequenceHeader = hevcSequenceHeader.slice(5);
|
|
|
|
do {
|
|
let hevc = {};
|
|
if (hevcSequenceHeader.length < 23) {
|
|
break;
|
|
}
|
|
|
|
hevc.configurationVersion = hevcSequenceHeader[0];
|
|
if (hevc.configurationVersion != 1) {
|
|
break;
|
|
}
|
|
hevc.general_profile_space = (hevcSequenceHeader[1] >> 6) & 0x03;
|
|
hevc.general_tier_flag = (hevcSequenceHeader[1] >> 5) & 0x01;
|
|
hevc.general_profile_idc = hevcSequenceHeader[1] & 0x1F;
|
|
hevc.general_profile_compatibility_flags = (hevcSequenceHeader[2] << 24) | (hevcSequenceHeader[3] << 16) | (hevcSequenceHeader[4] << 8) | hevcSequenceHeader[5];
|
|
hevc.general_constraint_indicator_flags = ((hevcSequenceHeader[6] << 24) | (hevcSequenceHeader[7] << 16) | (hevcSequenceHeader[8] << 8) | hevcSequenceHeader[9]);
|
|
hevc.general_constraint_indicator_flags = (hevc.general_constraint_indicator_flags << 16) | (hevcSequenceHeader[10] << 8) | hevcSequenceHeader[11];
|
|
hevc.general_level_idc = hevcSequenceHeader[12];
|
|
hevc.min_spatial_segmentation_idc = ((hevcSequenceHeader[13] & 0x0F) << 8) | hevcSequenceHeader[14];
|
|
hevc.parallelismType = hevcSequenceHeader[15] & 0x03;
|
|
hevc.chromaFormat = hevcSequenceHeader[16] & 0x03;
|
|
hevc.bitDepthLumaMinus8 = hevcSequenceHeader[17] & 0x07;
|
|
hevc.bitDepthChromaMinus8 = hevcSequenceHeader[18] & 0x07;
|
|
hevc.avgFrameRate = (hevcSequenceHeader[19] << 8) | hevcSequenceHeader[20];
|
|
hevc.constantFrameRate = (hevcSequenceHeader[21] >> 6) & 0x03;
|
|
hevc.numTemporalLayers = (hevcSequenceHeader[21] >> 3) & 0x07;
|
|
hevc.temporalIdNested = (hevcSequenceHeader[21] >> 2) & 0x01;
|
|
hevc.lengthSizeMinusOne = hevcSequenceHeader[21] & 0x03;
|
|
let numOfArrays = hevcSequenceHeader[22];
|
|
let p = hevcSequenceHeader.slice(23);
|
|
for (let i = 0; i < numOfArrays; i++) {
|
|
if (p.length < 3) {
|
|
brak;
|
|
}
|
|
let nalutype = p[0];
|
|
let n = (p[1]) << 8 | p[2];
|
|
// Logger.debug(nalutype, n);
|
|
p = p.slice(3);
|
|
for (let j = 0; j < n; j++) {
|
|
if (p.length < 2) {
|
|
break;
|
|
}
|
|
k = (p[0] << 8) | p[1];
|
|
// Logger.debug('k', k);
|
|
if (p.length < 2 + k) {
|
|
break;
|
|
}
|
|
p = p.slice(2);
|
|
if (nalutype == 33) {
|
|
//SPS
|
|
let sps = Buffer.alloc(k);
|
|
p.copy(sps, 0, 0, k);
|
|
// Logger.debug(sps, sps.length);
|
|
hevc.psps = HEVCParseSPS(sps, hevc);
|
|
info.profile = hevc.general_profile_idc;
|
|
info.level = hevc.general_level_idc / 30.0;
|
|
info.width = hevc.psps.pic_width_in_luma_samples - (hevc.psps.conf_win_left_offset + hevc.psps.conf_win_right_offset);
|
|
info.height = hevc.psps.pic_height_in_luma_samples - (hevc.psps.conf_win_top_offset + hevc.psps.conf_win_bottom_offset);
|
|
}
|
|
p = p.slice(k);
|
|
}
|
|
}
|
|
} while (0);
|
|
|
|
return info;
|
|
}
|
|
|
|
function readAVCSpecificConfig(avcSequenceHeader) {
|
|
let codec_id = avcSequenceHeader[0] & 0x0f;
|
|
if (codec_id == 7) {
|
|
return readH264SpecificConfig(avcSequenceHeader);
|
|
} else if (codec_id == 12) {
|
|
return readHEVCSpecificConfig(avcSequenceHeader);
|
|
}
|
|
}
|
|
|
|
|
|
function getAVCProfileName(info) {
|
|
switch (info.profile) {
|
|
case 1:
|
|
return 'Main';
|
|
case 2:
|
|
return 'Main 10';
|
|
case 3:
|
|
return 'Main Still Picture';
|
|
case 66:
|
|
return 'Baseline';
|
|
case 77:
|
|
return 'Main';
|
|
case 100:
|
|
return 'High';
|
|
default:
|
|
return '';
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
AUDIO_SOUND_RATE,
|
|
AUDIO_CODEC_NAME,
|
|
VIDEO_CODEC_NAME,
|
|
readAACSpecificConfig,
|
|
getAACProfileName,
|
|
readAVCSpecificConfig,
|
|
getAVCProfileName,
|
|
};
|