|
#!/usr/bin/env node |
|
|
|
printInfo(process.argv[2]); |
|
|
|
function printInfo(base64String) { |
|
if (!base64String) { |
|
return console.log('Base64 string should be provided'); |
|
} |
|
|
|
const spliceInfo = parseSpliceInfo(Buffer.from(base64String, 'base64')); |
|
if (spliceInfo) { |
|
console.log(JSON.stringify(spliceInfo, null, 4)); |
|
} |
|
} |
|
|
|
function parseSpliceInfo(buff) { |
|
let pos = 0, b = 0; |
|
const table_id = buff[pos++]; |
|
if (table_id !== 0xFC) { |
|
return console.error(`Unknown table_id: ${table_id.toString(16).toUpperCase()}`); |
|
} |
|
|
|
b = buff[pos++]; |
|
const section_syntax_indicator = b & 0x80; |
|
const private_indicator = b & 0x40; |
|
|
|
let left = b & 0x0F; |
|
let right = buff[pos++]; |
|
const section_length = ((left << 8) | right) & 0xFF; |
|
|
|
const protocol_version = buff[pos++]; |
|
|
|
b = buff[pos++]; |
|
const encrypted_packet = b & 0x80; |
|
const encryption_algorithm = b & 0x7E; |
|
|
|
left = b & 0x01; |
|
right = 0; |
|
[pos, right] = uimsbf(buff, pos, 4); |
|
const pts_adjustment = ((left << 32) | right) & 0x1FFFFFFFF; |
|
const cw_index = buff[pos++]; |
|
|
|
left = buff[pos++]; |
|
b = buff[pos++]; |
|
const tier = ((left << 4) | (b >> 4) & 0x0F) & 0x0FFF; |
|
|
|
right = buff[pos++]; |
|
const splice_command_length = ((b & 0x0F) << 8 | right) & 0x0FFF; |
|
|
|
let splice_command_type = buff[pos++]; |
|
let command = null; |
|
|
|
switch (splice_command_type) { |
|
case 0x00: |
|
splice_command_type = 'splice_null'; |
|
[pos, command] = splice_null(buff, pos); |
|
break; |
|
case 0x04: |
|
splice_command_type = 'splice_schedule'; |
|
[pos, command] = splice_schedule(buff, pos); |
|
break; |
|
case 0x05: |
|
splice_command_type = 'splice_insert'; |
|
[pos, command] = splice_insert(buff, pos); |
|
break; |
|
case 0x06: |
|
splice_command_type = 'time_signal'; |
|
[pos, command] = time_signal(buff, pos); |
|
break; |
|
case 0x07: |
|
splice_command_type = 'bandwidth_reservation'; |
|
[pos, command] = bandwidth_reservation(buff, pos); |
|
break; |
|
case 0xFF: |
|
splice_command_type = 'private_command'; |
|
[pos, command] = private_command(buff, pos); |
|
break; |
|
default: |
|
return console.error(`Unknown splice_command_type: ${splice_command_type.toString(16).toUpperCase()}`); |
|
} |
|
|
|
left = buff[pos++]; |
|
right = buff[pos++]; |
|
const descriptor_loop_length = ((left << 8) | right) & 0xFFFF; |
|
|
|
const splice_descriptors = []; |
|
for (let i = 0; i < descriptor_loop_length; i++) { |
|
let descriptor = null; |
|
[pos, descriptor] = splice_descriptor(buff, pos); |
|
splice_descriptors.push(descriptor); |
|
} |
|
|
|
return { |
|
table_id: `0x${table_id.toString(16).toUpperCase()}`, |
|
section_syntax_indicator, |
|
private_indicator, |
|
section_length, |
|
protocol_version, |
|
encrypted_packet, |
|
encryption_algorithm, |
|
pts_adjustment, |
|
cw_index, |
|
tier, |
|
splice_command_length, |
|
splice_command_type, |
|
command, |
|
splice_descriptors |
|
}; |
|
} |
|
|
|
function splice_null(buff, pos) { |
|
return [pos, null]; |
|
} |
|
|
|
function splice_schedule(buff, pos) { |
|
return [pos, null]; |
|
} |
|
|
|
function splice_insert(buff, pos) { |
|
let v = 0, b = 0; |
|
[pos, v] = uimsbf(buff, pos, 4); |
|
const splice_event_id = v >>> 0; |
|
b = buff[pos++]; |
|
const splice_event_cancel_indicator = (b >> 7) & 0x01; |
|
if (splice_event_cancel_indicator) { |
|
return [pos, { |
|
splice_event_id, |
|
splice_event_cancel_indicator |
|
}]; |
|
} |
|
b = buff[pos++]; |
|
const out_of_network_indicator = (b >> 7) & 0x01; |
|
const program_splice_flag = (b >> 6) & 0x01; |
|
const duration_flag = (b >> 5) & 0x01; |
|
const splice_immediate_flag = (b >> 4) & 0x01; |
|
let time = null; |
|
if (program_splice_flag && !splice_immediate_flag) { |
|
[pos, v] = splice_time(buff, pos); |
|
time = v; |
|
} |
|
let component_count = 0; |
|
const components = []; |
|
if (!program_splice_flag) { |
|
component_count = buff[pos++]; |
|
for (let i = 0; i < component_count; i++) { |
|
const component_tag = buff[pos++]; |
|
v = null; |
|
if (!splice_immediate_flag) { |
|
[pos, v] = splice_time(buff, pos); |
|
} |
|
components.push({ |
|
component_tag, |
|
component_value: v |
|
}); |
|
} |
|
} |
|
let duration = null; |
|
if (duration_flag) { |
|
[pos, duration] = break_duration(buff, pos); |
|
} |
|
[pos, v] = uimsbf(buff, pos, 2); |
|
const unique_program_id = v; |
|
const avail_num = buff[pos++]; |
|
const avails_expected = buff[pos++]; |
|
|
|
return [pos, { |
|
splice_event_id: `0x${splice_event_id.toString(16).toUpperCase()}`, |
|
splice_event_cancel_indicator, |
|
out_of_network_indicator, |
|
program_splice_flag, |
|
duration_flag, |
|
splice_immediate_flag, |
|
splice_time: time, |
|
component_count, |
|
components, |
|
duration, |
|
unique_program_id, |
|
avail_num, |
|
avails_expected |
|
}]; |
|
} |
|
|
|
function time_signal(buff, pos) { |
|
return [pos, null]; |
|
} |
|
|
|
function bandwidth_reservation(buff, pos) { |
|
return [pos, null]; |
|
} |
|
|
|
function private_command(buff, pos) { |
|
return [pos, null]; |
|
} |
|
|
|
function splice_descriptor(buff, pos) { |
|
const splice_descriptor_tag = buff[pos++]; |
|
const descriptor_length = buff[pos++]; |
|
let identifier = 0; |
|
[pos, identifier] = uimsbf(buff, pos, 4); |
|
const bytes = Buffer.alloc(descriptor_length); |
|
for (let i = 0; i < descriptor_length; i++) { |
|
bytes[i] = buff[pos++]; |
|
} |
|
return [pos, {splice_descriptor_tag, descriptor_length, identifier, private_bytes: bytes}]; |
|
} |
|
|
|
function splice_time(buff, pos) { |
|
const b = buff[pos++]; |
|
const time_specified_flag = (b >> 7) & 0x01; |
|
if (!time_specified_flag) { |
|
return [pos, {time_specified_flag}]; |
|
} |
|
const left = b & 0x01; |
|
let right = 0; |
|
[pos, right] = uimsbf(buff, pos, 4); |
|
const pts_time = (left << 32 | right) & 0x01FFFFFFFF; |
|
return [pos, {time_specified_flag, pts_time}]; |
|
} |
|
|
|
function break_duration(buff, pos) { |
|
const b = buff[pos++]; |
|
const auto_return = (b >> 7) & 0x01; |
|
const left = b & 0x01; |
|
let right = 0; |
|
[pos, right] = uimsbf(buff, pos, 4); |
|
const duration = (left << 32 | right) & 0x01FFFFFFFF; |
|
return [pos, {auto_return, duration}]; |
|
} |
|
|
|
function makeBitmask(length) { |
|
let mask = 0; |
|
for (let i = 0; i < length; i++) { |
|
mask |= 1 << i; |
|
} |
|
return mask; |
|
} |
|
|
|
function uimsbf(buff, pos, length) { |
|
let v = 0; |
|
for (let i = length - 1; i >= 0; i--) { |
|
v |= (buff[pos++] << 8 * i) & makeBitmask(8 * (i + 1)); |
|
} |
|
return [pos, v]; |
|
} |