Skip to content

Instantly share code, notes, and snippets.

@kuu
Last active April 27, 2020 10:52
Show Gist options
  • Save kuu/a40b71ac07287a43ca88717f562909db to your computer and use it in GitHub Desktop.
Save kuu/a40b71ac07287a43ca88717f562909db to your computer and use it in GitHub Desktop.
A script to parse SCTE-35 SpliceInfo message

Usage:

$ npx {url of this gist} {Base64 string of SCTE-35 SpliceInfo message}

Example:

$ npx https://gist.github.com/kuu/a40b71ac07287a43ca88717f562909db /DAlAAAAAAAAAP/wFAXwAAABf+/+cUHD8P4AFJPvAAABAQAAxVL2sw==
{
    "table_id": "0xFC",
    "section_syntax_indicator": 0,
    "private_indicator": 0,
    "section_length": 37,
    "protocol_version": 0,
    "encrypted_packet": 0,
    "encryption_algorithm": 0,
    "pts_adjustment": 0,
    "cw_index": 0,
    "tier": 4095,
    "splice_command_length": 20,
    "splice_command_type": "splice_insert",
    "command": {
        "splice_event_id": "0xF0000001",
        "splice_event_cancel_indicator": 0,
        "out_of_network_indicator": 1,
        "program_splice_flag": 1,
        "duration_flag": 1,
        "splice_immediate_flag": 0,
        "splice_time": {
            "time_specified_flag": 1,
            "pts_time": 1900135408
        },
        "component_count": 0,
        "components": [],
        "duration": {
            "auto_return": 1,
            "duration": 1348591
        },
        "unique_program_id": 0,
        "avail_num": 1,
        "avails_expected": 1
    },
    "splice_descriptors": []
}
#!/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];
}
{
"name": "parse-scte35-spliceinfo",
"version": "1.0.0",
"description": "A script to parse SCTE35 SpliceInfo message",
"bin": "./index.js",
"dependencies": {}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment