wireshark/epan/dissectors/packet-scte35.c
Guy Harris a4c8ebc18b Don't do any Decode As stuff for dissector tables not used with Decode As.
Have all dissector tables have a "supports Decode As" flag, which
defaults to FALSE, and which is set to TRUE if a register_decode_as()
refers to it.

When adding a dissector to a dissector table with a given key, only add
it for Decode As if the dissector table supports it.

For non-FT_STRING dissector tables, always check for multiple entries
for the same protocol with different dissectors, and report an error if
we found them.

This means there's no need for the creator of a dissector table to
specify whether duplicates of that sort should be allowed - we always do
the check when registering something for "Decode As" (in a non-FT_STRING
dissector table), and just don't bother registering anything for "Decode
As" if the dissector table doesn't support "Decode As", so there's no
check done for those dissector tables.

Change-Id: I4a1fdea3bddc2af27a65cfbca23edc99b26c0eed
Reviewed-on: https://code.wireshark.org/review/17402
Petri-Dish: Guy Harris <guy@alum.mit.edu>
Reviewed-by: Guy Harris <guy@alum.mit.edu>
2016-08-31 00:08:01 +00:00

1532 lines
58 KiB
C

/* packet-scte35.c
* Routines for SCTE-35 dissection
* Author: Ben Stewart <bst[at]google.com>
* Copyright 2016 Google Inc.
*
* The SCTE-35 protocol is described by the Society of Cable Telecommunications
* Engineers at <https://www.scte.org/documents/pdf/Standards/Top%20Ten/ANSI_SCTE%2035%202013.pdf>.
*
* This module implements a dissector for the main table in a SCTE-35 message, a
* splice_info_section. This payload is carried in a MPEG Section Table with a
* table ID of 0xFC. PIDs carrying this sort of table are also noted in the PMT
* with a stream type of 0x86, and a registration descriptor with fourcc 'CUEI'.
*
* The various splice command types are implemented in separate modules, and are
* linked to this dissector through the field scte35.splice_command_type. All
* field names follow the conventions documented in the SCTE35 specification.
*
* This dissector does not support encrypted SCTE35 messages, other than
* indicating through the scte35.encrypted_packet flag.
*
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include <epan/packet.h>
#define SCTE35_CMD_SPLICE_NULL (0x00)
#define SCTE35_CMD_SPLICE_SCHEDULE (0x04)
#define SCTE35_CMD_SPLICE_INSERT (0x05)
#define SCTE35_CMD_TIME_SIGNAL (0x06)
#define SCTE35_CMD_BANDWIDTH_RESERVATION (0x07)
#define SCTE35_CMD_PRIVATE_COMMAND (0xff)
#define SCTE35_AVAIL_DESCRIPTOR (0x00)
#define SCTE35_DTMF_DESCRIPTOR (0x01)
#define SCTE35_SEGMENTATION_DESCRIPTOR (0x02)
void proto_register_scte35(void);
void proto_register_scte35_private_command(void);
void proto_register_scte35_splice_insert(void);
void proto_register_scte35_splice_schedule(void);
void proto_register_scte35_time_signal(void);
void proto_reg_handoff_scte35(void);
void proto_reg_handoff_scte35_private_command(void);
void proto_reg_handoff_scte35_splice_insert(void);
void proto_reg_handoff_scte35_splice_schedule(void);
void proto_reg_handoff_scte35_time_signal(void);
/* MPEG Section Table ID for a SCTE35 splice_info_section. */
static const unsigned char SCTE35_TABLE_ID = 0xFCU;
/* Minimum length for a splice_info_section, excluding any splice commands,
* splice descriptors, encrypted CRC or alignment stuffing.
*/
static const int SCTE35_SI_MIN_LEN = 20;
/* Protocol handle */
static int proto_scte35 = -1;
/* Dissector table for scte35.splice_command_type */
static dissector_table_t scte35_cmd_dissector_table = NULL;
/* splice_info_section table */
static gint ett_scte35_splice_info_section = -1;
/* splice_info_section fields */
static gint hf_table_id = -1;
static gint hf_section_syntax_indicator = -1;
static gint hf_private_indicator = -1;
static gint hf_reserved = -1;
static gint hf_section_length = -1;
static gint hf_protocol_version = -1;
static gint hf_encrypted_packet = -1;
static gint hf_encryption_algorithm = -1;
static gint hf_pts_adjustment = -1;
static gint hf_cw_index = -1;
static gint hf_tier = -1;
static gint hf_splice_command_length = -1;
static gint hf_splice_command_type = -1;
static gint hf_descriptor_loop_length = -1;
static gint hf_splice_descriptor_tag = -1;
static gint hf_splice_descriptor_length = -1;
static gint hf_splice_descriptor_identifier = -1;
static gint hf_descriptor_provider_avail_id = -1;
static gint hf_descriptor_preroll = -1;
static gint hf_descriptor_dtmf_count = -1;
static gint hf_descriptor_dtmf_reserved = -1;
static gint hf_descriptor_dtmf = -1;
static gint hf_descriptor_event_id = -1;
static gint hf_descriptor_cancel_indicator = -1;
static gint hf_descriptor_reserved0 = -1;
static gint hf_descriptor_psf = -1;
static gint hf_descriptor_segmentation_duration_flag = -1;
static gint hf_descriptor_delivery_not_restricted_flag = -1;
static gint hf_descriptor_web_delivery_allowed_flag = -1;
static gint hf_descriptor_no_regional_blackout_flag = -1;
static gint hf_descriptor_archive_allow_flag = -1;
static gint hf_descriptor_device_restrictions = -1;
static gint hf_descriptor_reserved1 = -1;
static gint hf_descriptor_component_count = -1;
static gint hf_descriptor_component_tag = -1;
static gint hf_descriptor_component_reserved = -1;
static gint hf_descriptor_component_pts_offset = -1;
static gint hf_descriptor_segmentation_duration = -1;
static gint hf_descriptor_segmentation_upid_type = -1;
static gint hf_descriptor_segmentation_upid_length = -1;
static gint hf_descriptor_segmentation_upid = -1;
static gint hf_descriptor_segmentation_type_id = -1;
static gint hf_descriptor_segment_num = -1;
static gint hf_descriptor_segments_expected = -1;
static gint hf_e_crc32 = -1;
static gint hf_crc32 = -1;
/* time_signal protocol and fields */
static int proto_scte35_time = -1;
static gint ett_scte35_time_signal = -1;
static gint ett_scte35_time_signal_splice_time = -1;
static gint hf_time_specified = -1;
static gint hf_time_reserved = -1;
static gint hf_time_pts = -1;
/* private_command protocol and fields */
static int proto_private_command = -1;
static gint ett_private_command = -1;
static gint hf_identifier = -1;
static gint hf_private_byte = -1;
/* Dissector table for scte35_private_command.identifier */
static dissector_table_t private_identifier_table = NULL;
static dissector_handle_t scte35_handle = NULL;
/* splice_insert protocol and fields */
static int proto_scte35_si = -1;
static gint ett_scte35_splice_insert = -1;
static gint hf_splice_insert_event_id = -1;
static gint hf_splice_cancel_indicator = -1;
static gint hf_reserved0 = -1;
static gint hf_out_of_network_indicator = -1;
static gint hf_program_splice_flag = -1;
static gint hf_duration_flag = -1;
static gint hf_splice_immediate_flag = -1;
static gint hf_reserved1 = -1;
static gint hf_splice_time_specified_flag = -1;
static gint hf_splice_time_reserved = -1;
static gint hf_splice_time_pts_time = -1;
static gint hf_component_count = -1;
static gint hf_component_tag = -1;
static gint hf_component_splice_time_tsf = -1;
static gint hf_component_splice_time_reserved = -1;
static gint hf_component_splice_time_pts_time = -1;
static gint hf_break_duration_auto_return = -1;
static gint hf_break_duration_reserved = -1;
static gint hf_break_duration_duration = -1;
static gint hf_unique_program_id = -1;
static gint hf_avail_num = -1;
static gint hf_avails_expected = -1;
/* splice_schedule protocol and fields */
static int proto_scte35_splice_schedule = -1;
static gint ett_scte35_splice_schedule = -1;
static gint hf_splice_count = -1;
static gint hf_splice_event_id = -1;
static gint hf_splice_event_cancel_indicator = -1;
static gint hf_splice_reserved0 = -1;
static gint hf_splice_out_of_network = -1;
static gint hf_splice_program_splice_flag = -1;
static gint hf_splice_duration_flag = -1;
static gint hf_splice_reserved1 = -1;
static gint hf_splice_utc_splice_time = -1;
static gint hf_splice_component_count = -1;
static gint hf_splice_component_tag = -1;
static gint hf_splice_component_utc_splice_time = -1;
static gint hf_splice_break_duration_auto_return = -1;
static gint hf_splice_break_duration_reserved = -1;
static gint hf_splice_break_duration_duration = -1;
static gint hf_splice_unique_program_id = -1;
static gint hf_splice_avail_num = -1;
static gint hf_splice_avails_expected = -1;
static const true_false_string tfs_section_syntax_indicator = {
"Reserved", "MPEG short sections in use"};
static const true_false_string tfs_private_indicator = {
"Reserved", "Mandatory value"};
static const true_false_string tfs_encrypted_packet = {
"Encrypted data", "Cleartext"};
static const true_false_string tfs_descriptor_cancel_indicator = {
"Cancel Request", "New or existing event"};
static const true_false_string tfs_descriptor_psf = {
"All PIDs to be spliced", "Component Splice Mode"};
static const true_false_string tfs_descriptor_sdf = {
"Segmentation duration present", "No duration present"};
static const true_false_string tfs_descriptor_dnr = {
"No delivery restrictions", "Restricted delivery"};
static const true_false_string tfs_descriptor_web = {
"Permitted", "Restricted"};
static const true_false_string tfs_descriptor_blackout = {
"No regional blackouts", "Regional restrictions"};
static const true_false_string tfs_descriptor_archive = {
"No recording restrictions", "Recording is restricted"};
static const range_string rv_splice_command_type[] = {
{ SCTE35_CMD_SPLICE_NULL, SCTE35_CMD_SPLICE_NULL, "splice_null" },
{ 0x01, 0x03, "Reserved" },
{ SCTE35_CMD_SPLICE_SCHEDULE, SCTE35_CMD_SPLICE_SCHEDULE, "splice_schedule" },
{ SCTE35_CMD_SPLICE_INSERT, SCTE35_CMD_SPLICE_INSERT, "splice_insert" },
{ SCTE35_CMD_TIME_SIGNAL, SCTE35_CMD_TIME_SIGNAL, "time_signal" },
{ SCTE35_CMD_BANDWIDTH_RESERVATION, SCTE35_CMD_BANDWIDTH_RESERVATION, "bandwidth_reservation" },
{ 0x08, 0xfe, "Reserved" },
{ SCTE35_CMD_PRIVATE_COMMAND, SCTE35_CMD_PRIVATE_COMMAND, "private_command" },
{ 0, 0, NULL }
};
static const range_string rv_splice_descriptor_tag[] = {
{ SCTE35_AVAIL_DESCRIPTOR, SCTE35_AVAIL_DESCRIPTOR, "avail_descriptor" },
{ SCTE35_DTMF_DESCRIPTOR, SCTE35_DTMF_DESCRIPTOR, "DTMF_descriptor" },
{ SCTE35_SEGMENTATION_DESCRIPTOR, SCTE35_SEGMENTATION_DESCRIPTOR, "segmentation_descriptor" },
{ 0x03, 0xff, "Reserved" },
{ 0, 0, NULL }
};
static const range_string scte35_device_restrictions[] = {
{ 0x00, 0x00, "Restrict Group 0" },
{ 0x01, 0x01, "Restrict Group 1" },
{ 0x02, 0x02, "Restrict Group 2" },
{ 0x03, 0x03, "No Restrictions" },
{ 0, 0, NULL }
};
static const range_string scte35_segmentation_upid_type[] = {
{ 0x00, 0x00, "Not Used" },
{ 0x01, 0x01, "User Defined (deprecated)" },
{ 0x02, 0x02, "ISCI" },
{ 0x03, 0x03, "Ad-ID" },
{ 0x04, 0x04, "UMID (SMPTE 330M)" },
{ 0x05, 0x05, "ISAN" },
{ 0x06, 0x06, "Versioned ISAN" },
{ 0x07, 0x07, "Tribune TID" },
{ 0x08, 0x08, "Turner Identifier" },
{ 0x09, 0x09, "CableLabs ADI Identifier" },
{ 0x0a, 0x0a, "EIDR" },
{ 0x0b, 0x0b, "ATSC A57/B Content Identifier" },
{ 0x0c, 0x0c, "Managed Private UPID" },
{ 0x0d, 0x0d, "Multiple UPIDs" },
{ 0x0e, 0xff, "Reserved" },
{ 0, 0, NULL }
};
static const range_string scte35_segmentation_type_id[] = {
{ 0x00, 0x00, "Not Indicated" },
{ 0x01, 0x01, "Content Identification" },
{ 0x10, 0x10, "Program Start" },
{ 0x11, 0x11, "Program End" },
{ 0x12, 0x12, "Program Early Termination" },
{ 0x13, 0x13, "Program Breakaway" },
{ 0x14, 0x14, "Program Resumption" },
{ 0x15, 0x15, "Program Runover Planned" },
{ 0x16, 0x16, "Program Runover Unplanned" },
{ 0x17, 0x17, "Program Overlap Start" },
{ 0x20, 0x20, "Chapter Start" },
{ 0x21, 0x21, "Chapter End" },
{ 0x30, 0x30, "Provider Advertisement Start" },
{ 0x31, 0x31, "Provider Advertisement End" },
{ 0x32, 0x32, "Distributor Advertisement Start" },
{ 0x33, 0x33, "Distributor Advertisement End" },
{ 0x34, 0x34, "Placement Opportunity Start" },
{ 0x35, 0x35, "Placement Opportunity End" },
{ 0x40, 0x40, "Unscheduled Event Start" },
{ 0x41, 0x41, "Unscheduled Event End" },
{ 0, 0, NULL }
};
static const range_string rv_encryption_algorithm[] = {
{ 0x00, 0x00, "No encryption"},
{ 0x01, 0x01, "DES - ECB mode"},
{ 0x02, 0x02, "DES - CBC mode"},
{ 0x03, 0x03, "Triple DES EDE3 - ECB mode"},
{ 0x04, 0x1F, "Reserved"},
{ 0x20, 0x3F, "User private"},
{ 0, 0, NULL }
};
/* time_signal dissector */
static int
dissect_scte35_time_signal(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
gint tvb_len, min_length = 1, offset = 0;
guint8 time_specified_flag;
proto_item *ti;
proto_tree *time_tree;
/* Check packet length. */
tvb_len = (gint)tvb_reported_length(tvb);
if (tvb_len < min_length)
return 0;
time_specified_flag = tvb_get_guint8(tvb, offset) & 0x80;
if (time_specified_flag)
min_length += 4;
if (tvb_len < min_length)
return 0;
/* Set up headers in the packet list */
col_add_fstr(pinfo->cinfo, COL_INFO, "Time Signal (%s)",
time_specified_flag ? "Future" : "Immediate");
/* Create a subtee for the time_signal */
ti = proto_tree_add_item(tree, proto_scte35_time, tvb, 0, -1, ENC_NA);
time_tree = proto_item_add_subtree(ti, ett_scte35_time_signal);
/* Parse out the fields. */
proto_tree_add_item(time_tree, hf_time_specified, tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(time_tree, hf_time_reserved, tvb, offset, 1, ENC_BIG_ENDIAN);
if (time_specified_flag) {
proto_tree_add_item(time_tree, hf_time_pts, tvb, offset, 5, ENC_BIG_ENDIAN);
offset += 4;
}
offset += 1;
return offset;
}
void
proto_register_scte35_time_signal(void)
{
static gint *ett[] = {
&ett_scte35_time_signal,
&ett_scte35_time_signal_splice_time,
};
static hf_register_info hf[] = {
{&hf_time_specified,
{"Time Specified", "scte35_time.splice.time_specified", FT_BOOLEAN, 8,
NULL, 0x80, NULL, HFILL}},
{&hf_time_reserved,
{"Reserved", "scte35_time.splice.reserved", FT_UINT8, BASE_HEX,
NULL, 0x7E, NULL, HFILL}},
{&hf_time_pts,
{"PTS Time", "scte35_time.splice.pts", FT_UINT64, BASE_DEC,
NULL, G_GUINT64_CONSTANT(0x1FFFFFFFF), NULL, HFILL}},
};
proto_scte35_time = proto_register_protocol("SCTE-35 Time Signal", "SCTE35 TS", "scte35_time");
proto_register_subtree_array(ett, array_length(ett));
proto_register_field_array(proto_scte35_time, hf, array_length(hf));
}
void
proto_reg_handoff_scte35_time_signal(void)
{
dissector_handle_t scte35_time_handle;
/* Create a dissector for time_signal packets. */
scte35_time_handle = create_dissector_handle(dissect_scte35_time_signal, proto_scte35_time);
/* And hook it up to SCTE-35 messages. */
dissector_add_uint("scte35.splice_command_type", SCTE35_CMD_TIME_SIGNAL, scte35_time_handle);
}
/* scte35 private_command dissector */
static int
dissect_scte35_private_command(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
gint tvb_len;
guint32 identifier;
gint offset = 0;
proto_item *ti;
proto_tree *pc_tree;
tvb_len = (gint)tvb_reported_length(tvb);
if (tvb_len < 4)
return 0;
/* Display rudimentary header information. */
ti = proto_tree_add_item(tree, proto_private_command, tvb, 0, -1, ENC_NA);
pc_tree = proto_item_add_subtree(ti, ett_private_command);
proto_tree_add_item_ret_uint(pc_tree, hf_identifier, tvb, offset, 4, ENC_BIG_ENDIAN, &identifier);
col_add_fstr(pinfo->cinfo, COL_INFO, "Private Command (0x%08x)", identifier);
offset += 4;
proto_tree_add_item(pc_tree, hf_private_byte, tvb, offset, -1, ENC_NA);
/* Let another dissector try to decode this data. */
dissector_try_uint(private_identifier_table, identifier, tvb, pinfo, tree);
return tvb_len;
}
void
proto_register_scte35_private_command(void)
{
static gint *ett[] = {
&ett_private_command,
};
static hf_register_info hf[] = {
{&hf_identifier,
{"Identifier", "scte35_private_command.identifier", FT_UINT32,
BASE_HEX, NULL, 0, NULL, HFILL}},
{&hf_private_byte,
{"Private Bytes", "scte35_private_command.private_byte", FT_BYTES, 0,
NULL, 0, NULL, HFILL}},
};
proto_private_command = proto_register_protocol("SCTE-35 Private Command", "SCTE35 PC", "scte35_private_command");
proto_register_subtree_array(ett, array_length(ett));
proto_register_field_array(proto_private_command, hf, array_length(hf));
/* Allow other modules to hook private commands and decode them. */
private_identifier_table = register_dissector_table(
"scte35_private_command.identifier", "SCTE-35 Private Command Identifier",
proto_private_command, FT_UINT32, BASE_HEX);
}
void
proto_reg_handoff_scte35_private_command(void)
{
dissector_handle_t scte35_private_command_handle;
/* Create a dissector for private commands. */
scte35_private_command_handle = create_dissector_handle(dissect_scte35_private_command, proto_private_command);
/* Trigger our dissector on any private_command SCTE-35 messages. */
dissector_add_uint("scte35.splice_command_type", SCTE35_CMD_PRIVATE_COMMAND, scte35_private_command_handle);
}
/* scte35 splice_insert dissector */
static int
dissect_component(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint8 sif, int idx)
{
gint offset = 0;
guint8 component_tag, tsf;
proto_tree *component_tree;
gint tvb_len, min_length = sif ? 1 : 2;
tvb_len = (gint)tvb_reported_length(tvb);
if (tvb_len < min_length)
return 0;
if (!sif) {
/* Check whether time is present in the component. */
tsf = tvb_get_guint8(tvb, offset + 1) & 0x80;
if (tsf) {
min_length += 4;
if (tvb_len < min_length)
return 0;
}
}
/* Create a subtree for the component. */
component_tag = tvb_get_guint8(tvb, offset);
proto_tree_add_subtree_format(
tree, tvb, offset, min_length, idx, &component_tree,
"Component %d (0x%02x)", idx, component_tag);
/* Parse out component flags. */
proto_tree_add_item(component_tree, hf_component_tag, tvb, offset, 1, ENC_BIG_ENDIAN);
offset++;
/* For non-immediate splices.. */
if (!sif) {
proto_tree_add_item(component_tree, hf_component_splice_time_tsf, tvb,
offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(component_tree, hf_component_splice_time_reserved,
tvb, offset, 1, ENC_BIG_ENDIAN);
/* And the PTS if present. */
if (tsf) {
proto_tree_add_item(component_tree, hf_component_splice_time_pts_time,
tvb, offset, 5, ENC_BIG_ENDIAN);
offset += 4;
}
offset++;
}
return offset;
}
static int
dissect_scte35_splice_insert(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
gint tvb_len, min_length = 5, dissected_length;
guint8 cancel_flag, psf, df, sif, tsf, component_count;
guint32 event_id;
gint component;
gint offset = 0;
proto_item *ti;
proto_tree *si_tree, *st_tree;
static const int *new_event_fields[] = {
&hf_out_of_network_indicator,
&hf_program_splice_flag,
&hf_duration_flag,
&hf_splice_immediate_flag,
&hf_reserved1,
NULL
};
/* Check with no optional subfields */
tvb_len = (gint)tvb_reported_length(tvb);
if (tvb_len < min_length)
return 0;
cancel_flag = tvb_get_guint8(tvb, offset + 4) & 0x80;
event_id = tvb_get_ntohl(tvb, 0);
if (!cancel_flag) {
min_length += 5;
if (tvb_len < min_length)
return 0;
}
/* Set up headers in the packet list */
col_add_fstr(pinfo->cinfo, COL_INFO, "Splice %s Event 0x%08x",
cancel_flag ? "Cancellation" : "Insertion", event_id);
/* Create a root tree element for the splice_insert protocol. */
ti = proto_tree_add_item(tree, proto_scte35_si, tvb, 0, -1, ENC_NA);
si_tree = proto_item_add_subtree(ti, ett_scte35_splice_insert);
/* Parse header fields */
proto_tree_add_item(si_tree, hf_splice_insert_event_id, tvb, offset, 4, ENC_BIG_ENDIAN);
offset += 4;
proto_tree_add_item(si_tree, hf_splice_cancel_indicator, tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(si_tree, hf_reserved0, tvb, offset, 1, ENC_BIG_ENDIAN);
offset++;
/* Check for a new event. */
if (!cancel_flag) {
/* Parse out the 'new event' fields. */
psf = tvb_get_guint8(tvb, offset) & 0x40;
df = tvb_get_guint8(tvb, offset) & 0x20;
sif = tvb_get_guint8(tvb, offset) & 0x10;
proto_tree_add_bitmask_list(si_tree, tvb, offset, 1, new_event_fields, ENC_BIG_ENDIAN);
offset++;
/* Parse out the program-level splice fields. */
if (psf && !sif) {
min_length += 1;
if (tvb_len < min_length)
return offset;
tsf = tvb_get_bits8(tvb, offset * 8, 1);
proto_tree_add_subtree(si_tree, tvb, offset, tsf ? 5 : 1, 0, &st_tree, "Program Splice Time");
proto_tree_add_item(st_tree, hf_splice_time_specified_flag, tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(st_tree, hf_splice_time_reserved, tvb, offset, 1, ENC_BIG_ENDIAN);
/* If a time is specified, display it too. */
if (tsf) {
min_length += 4;
if (tvb_len < min_length)
return offset;
proto_tree_add_item(st_tree, hf_splice_time_pts_time, tvb, offset, 5, ENC_BIG_ENDIAN);
offset += 4;
}
offset++;
}
/* For component-level splices, parse the component table. */
if (!psf) {
min_length += 1;
if (tvb_len < min_length)
return offset;
component_count = tvb_get_guint8(tvb, offset);
proto_tree_add_item(si_tree, hf_component_count, tvb, offset, 1, ENC_BIG_ENDIAN);
offset++;
min_length += component_count * (sif ? 1 : 2);
if (tvb_len < min_length)
return offset;
/* Dissect each splice component. */
for (component = 0; component < component_count; ++component) {
dissected_length = dissect_component(
tvb_new_subset_remaining(tvb, offset),
pinfo, si_tree, sif, component);
/* Propagate failures. */
if (dissected_length < 1)
return offset;
offset += dissected_length;
}
}
/* If present, parse out the duration field. */
if (df) {
min_length += 5;
if (tvb_len < min_length)
return offset;
proto_tree_add_item(si_tree, hf_break_duration_auto_return, tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(si_tree, hf_break_duration_reserved, tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(si_tree, hf_break_duration_duration, tvb, offset, 5, ENC_BIG_ENDIAN);
offset += 5;
}
/* Parse the UPID and avails fields. */
proto_tree_add_item(si_tree, hf_unique_program_id, tvb, offset, 2, ENC_BIG_ENDIAN);
offset += 2;
proto_tree_add_item(si_tree, hf_avail_num, tvb, offset, 1, ENC_BIG_ENDIAN);
offset++;
proto_tree_add_item(si_tree, hf_avails_expected, tvb, offset, 1, ENC_BIG_ENDIAN);
offset++;
}
return offset;
}
void
proto_register_scte35_splice_insert(void)
{
static hf_register_info hf[] = {
{&hf_splice_insert_event_id,
{"Event ID", "scte35_si.event_id", FT_UINT32, BASE_HEX,
NULL, 0, NULL, HFILL}},
{&hf_splice_cancel_indicator,
{"Cancelled", "scte35_si.cancelled", FT_BOOLEAN, 8,
NULL, 0x80, NULL, HFILL}},
{&hf_reserved0,
{"Reserved", "scte35_si.reserved0", FT_UINT8, 1,
NULL, 0x7F, NULL, HFILL}},
{&hf_out_of_network_indicator,
{"Out of Network", "scte35_si.out_of_net", FT_BOOLEAN, 8,
NULL, 0x80, NULL, HFILL}},
{&hf_program_splice_flag,
{"Program Splice Point", "scte35_si.psf", FT_BOOLEAN, 8,
NULL, 0x40, NULL, HFILL}},
{&hf_duration_flag,
{"Duration Present", "scte35_si.duration_flag", FT_BOOLEAN, 8,
NULL, 0x20, NULL, HFILL}},
{&hf_splice_immediate_flag,
{"Splice Immediate", "scte35_si.splice_immediate", FT_BOOLEAN, 8,
NULL, 0x10, NULL, HFILL}},
{&hf_reserved1,
{"Reserved", "scte35_si.reserved1", FT_UINT8, 1,
NULL, 0x0f, NULL, HFILL}},
{&hf_splice_time_specified_flag,
{"Time Specified", "scte35_si.splice_time.time_specified", FT_BOOLEAN,
8, NULL, 0x80, NULL, HFILL}},
{&hf_splice_time_reserved,
{"Reserved", "scte35_si.splice_time.reserved", FT_UINT8, 1,
NULL, 0x7E, NULL, HFILL}},
{&hf_splice_time_pts_time,
{"PTS Time", "scte35_si.splice_time.pts", FT_UINT64, 5,
NULL, G_GUINT64_CONSTANT(0x1FFFFFFFF), NULL, HFILL}},
{&hf_component_count,
{"Component Count", "scte35_si.component_count", FT_UINT8, BASE_DEC,
NULL, 0, NULL, HFILL}},
{&hf_component_tag,
{"Component Tag", "scte35_si.component.tag", FT_UINT8, BASE_HEX,
NULL, 0, NULL, HFILL}},
{&hf_component_splice_time_tsf,
{"Time Specified", "scte35_si.component.time_specified", FT_BOOLEAN, 8,
NULL, 0x80, NULL, HFILL}},
{&hf_component_splice_time_reserved,
{"Reserved", "scte35_si.component.reserved", FT_UINT8, 1,
NULL, 0x7E, NULL, HFILL}},
{&hf_component_splice_time_pts_time,
{"PTS Time", "scte35_si.component.pts", FT_UINT64, 5,
NULL, G_GUINT64_CONSTANT(0x1FFFFFFFF), NULL, HFILL}},
{&hf_break_duration_auto_return,
{"Auto Return", "scte35_si.break.auto_return", FT_BOOLEAN, 8,
NULL, 0x80, NULL, HFILL}},
{&hf_break_duration_reserved,
{"Reserved", "scte35_si.break.reserved", FT_UINT8, 1,
NULL, 0x7E, NULL, HFILL}},
{&hf_break_duration_duration,
{"Duration", "scte35_si.break.duration", FT_UINT64, 5,
NULL, G_GUINT64_CONSTANT(0x1FFFFFFFF), NULL, HFILL}},
{&hf_unique_program_id,
{"Unique Program ID", "scte35_si.upid", FT_UINT16, BASE_HEX,
NULL, 0, NULL, HFILL}},
{&hf_avail_num,
{"Avail Number", "scte35_si.avail", FT_UINT8, BASE_DEC,
NULL, 0, NULL, HFILL}},
{&hf_avails_expected,
{"Avails Expected", "scte35_si.avails_expected", FT_UINT8, BASE_DEC,
NULL, 0, NULL, HFILL}},
};
static gint *ett[] = {
&ett_scte35_splice_insert,
};
proto_scte35_si = proto_register_protocol("SCTE-35 Splice Insert", "SCTE35 SI", "scte35_si");
proto_register_subtree_array(ett, array_length(ett));
proto_register_field_array(proto_scte35_si, hf, array_length(hf));
}
void
proto_reg_handoff_scte35_splice_insert(void)
{
dissector_handle_t scte35_si_handle;
/* Create a splice_insert dissector, and hook it into SCTE35 parsing. */
scte35_si_handle = create_dissector_handle(dissect_scte35_splice_insert, proto_scte35_si);
dissector_add_uint("scte35.splice_command_type", SCTE35_CMD_SPLICE_INSERT, scte35_si_handle);
}
/* scte35 splice_schedule dissector */
static int
dissect_scte35_splice_schedule(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
gint tvb_len, min_length = 1;
guint8 splice_count, cancel_flag, psf, df, component_count;
gint component, splice;
gint offset = 0, splice_length;
proto_item *ti;
proto_tree *ss_tree, *sp_tree, *component_tree;
static const int *splice_event_flags[] = {
&hf_splice_out_of_network,
&hf_splice_program_splice_flag,
&hf_splice_duration_flag,
&hf_splice_reserved1,
NULL
};
/* Check with no optional subfields */
tvb_len = (gint)tvb_reported_length(tvb);
if (tvb_len < min_length)
return 0;
/* Set up headers in the packet list */
splice_count = tvb_get_guint8(tvb, 0);
min_length += splice_count * 5;
if (tvb_len < min_length)
return 0;
col_add_fstr(pinfo->cinfo, COL_INFO, "Splice Schedule (%d splices)", splice_count);
/* Create the root of the dissection */
ti = proto_tree_add_item(tree, proto_scte35_splice_schedule, tvb, 0, -1, ENC_NA);
ss_tree = proto_item_add_subtree(ti, ett_scte35_splice_schedule);
/* Header fields for splice_schedule() message */
proto_tree_add_item(ss_tree, hf_splice_count, tvb, offset, 1, ENC_NA);
offset++;
/* Process each splice. */
for (splice = 0; splice < splice_count; ++splice) {
cancel_flag = tvb_get_bits8(tvb, offset * 8 + 32, 1);
psf = cancel_flag ? 0 : tvb_get_bits8(tvb, offset * 8 + 41, 1);
df = cancel_flag ? 0 : tvb_get_bits8(tvb, offset * 8 + 42, 1);
component_count = cancel_flag ? 0 : (psf ? 0 : tvb_get_guint8(tvb, offset + 6));
splice_length = 5;
if (!cancel_flag)
splice_length += 4 + 1;
if (!cancel_flag && psf)
splice_length += 4;
if (!cancel_flag && !psf)
splice_length += 1 + 5 * component_count;
if (!cancel_flag && df)
splice_length += 5;
/* Add a subtree for the splice. */
proto_tree_add_subtree_format(
ss_tree, tvb, offset, splice_length, splice, &sp_tree,
"Splice %d", splice);
/* Show the splice header. */
proto_tree_add_item(ss_tree, hf_splice_event_id, tvb, offset, 4, ENC_BIG_ENDIAN);
offset += 4;
proto_tree_add_item(ss_tree, hf_splice_event_cancel_indicator, tvb,
offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(ss_tree, hf_splice_reserved0, tvb, offset, 1, ENC_BIG_ENDIAN);
offset++;
if (!cancel_flag) {
min_length += 5;
if (tvb_len < min_length)
return offset;
df = tvb_get_bits8(tvb, offset * 8 + 2, 1);
/* Parse out the splice event flags. */
proto_tree_add_bitmask_list(ss_tree, tvb, offset, 1, splice_event_flags, ENC_BIG_ENDIAN);
offset++;
min_length += (psf ? 4 : 1);
if (tvb_len < min_length)
return offset;
if (psf) {
proto_tree_add_item(ss_tree, hf_splice_utc_splice_time, tvb,
offset, 4, ENC_BIG_ENDIAN);
offset += 4;
} else {
component_count = tvb_get_guint8(tvb, offset);
proto_tree_add_item(ss_tree, hf_splice_component_count, tvb, offset, 1, ENC_NA);
offset++;
min_length += 5 * component_count;
if (tvb_len < min_length)
return offset;
/* Parse out each component stream. */
for (component = 0; component < component_count; ++component) {
proto_tree_add_subtree_format(sp_tree, tvb, offset, 5, component, &component_tree,
"Component %d", component);
proto_tree_add_item(component_tree, hf_splice_component_tag, tvb,
offset, 1, ENC_NA);
offset++;
proto_tree_add_item(component_tree, hf_splice_component_utc_splice_time, tvb,
offset, 4, ENC_NA);
offset += 4;
}
}
/* Parse out break duration, if present. */
if (df) {
min_length += 5;
if (tvb_len < min_length)
return offset;
proto_tree_add_item(ss_tree, hf_splice_break_duration_auto_return, tvb,
offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(ss_tree, hf_splice_break_duration_reserved, tvb,
offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(ss_tree, hf_splice_break_duration_duration, tvb,
offset, 5, ENC_BIG_ENDIAN);
offset += 5;
}
}
proto_tree_add_item(ss_tree, hf_splice_unique_program_id, tvb, offset, 2, ENC_BIG_ENDIAN);
offset += 2;
proto_tree_add_item(ss_tree, hf_splice_avail_num, tvb, offset, 1, ENC_BIG_ENDIAN);
offset++;
proto_tree_add_item(ss_tree, hf_splice_avails_expected, tvb, offset, 1, ENC_BIG_ENDIAN);
offset++;
}
return offset;
}
void
proto_register_scte35_splice_schedule(void)
{
static hf_register_info hf[] = {
{&hf_splice_count,
{"Splice Count", "scte35_splice_schedule.splice_count",
FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL}},
{&hf_splice_event_id,
{"Event ID", "scte35_splice_schedule.splice.event_id",
FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL}},
{&hf_splice_event_cancel_indicator,
{"Event Cancel Indicator", "scte35_splice_schedule.splice.event_cancel_indicator",
FT_BOOLEAN, 8, NULL, 0x80, NULL, HFILL}},
{&hf_splice_reserved0,
{"Reserved", "scte35_splice_schedule.splice.reserved0",
FT_UINT8, BASE_HEX, NULL, 0x7F, NULL, HFILL}},
{&hf_splice_out_of_network,
{"Out of Network Indicator", "scte35_splice_schedule.splice.out_of_network_indicator",
FT_BOOLEAN, 8, NULL, 0x80, NULL, HFILL}},
{&hf_splice_program_splice_flag,
{"Program Splice Flag", "scte35_splice_schedule.splice.program_splice_flag",
FT_BOOLEAN, 8, NULL, 0x40, NULL, HFILL}},
{&hf_splice_duration_flag,
{"Duration Flag", "scte35_splice_schedule.splice.duration_flag",
FT_BOOLEAN, 8, NULL, 0x20, NULL, HFILL}},
{&hf_splice_reserved1,
{"Reserved", "scte35_splice_schedule.splice.reserved1",
FT_UINT8, BASE_HEX, NULL, 0x1F, NULL, HFILL}},
{&hf_splice_utc_splice_time,
{"UTC Splice Time", "scte35_splice_schedule.splice.utc_splice_time",
FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL}},
{&hf_splice_component_count,
{"Component Count", "scte35_splice_schedule.splice.component_count",
FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL}},
{&hf_splice_component_tag,
{"Component Tag", "scte35_splice_schedule.splice.component.tag",
FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL}},
{&hf_splice_component_utc_splice_time,
{"UTC Splice Time", "scte35_splice_schedule.splice.component.utc_splice_time",
FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL}},
{&hf_splice_break_duration_auto_return,
{"Auto Return", "scte35_splice_schedule.splice.break_duration.auto_return",
FT_BOOLEAN, 8, NULL, 0x80, NULL, HFILL}},
{&hf_splice_break_duration_reserved,
{"Reserved", "scte35_splice_schedule.splice.break_duration.reserved",
FT_UINT8, BASE_HEX, NULL, 0x7E, NULL, HFILL}},
{&hf_splice_break_duration_duration,
{"Duration", "scte35_splice_schedule.splice.break_duration.duration",
FT_UINT64, BASE_DEC, NULL, G_GUINT64_CONSTANT(0x1FFFFFFFF), NULL, HFILL}},
{&hf_splice_unique_program_id,
{"Unique Program ID", "scte35_splice_schedule.splice.unique_program_id",
FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL}},
{&hf_splice_avail_num,
{"Avail Number", "scte35_splice_schedule.splice.avail_num",
FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL}},
{&hf_splice_avails_expected,
{"Avails Expected", "scte35_splice_schedule.splice.avails_expected",
FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL}},
};
static gint *ett[] = {
&ett_scte35_splice_schedule,
};
proto_scte35_splice_schedule = proto_register_protocol("SCTE-35 Splice Schedule", "SCTE35 SS", "scte35_splice_schedule");
proto_register_subtree_array(ett, array_length(ett));
proto_register_field_array(proto_scte35_splice_schedule, hf, array_length(hf));
}
void
proto_reg_handoff_scte35_splice_schedule(void)
{
dissector_handle_t dissector;
dissector = create_dissector_handle(dissect_scte35_splice_schedule, proto_scte35_splice_schedule);
dissector_add_uint("scte35.splice_command_type", SCTE35_CMD_SPLICE_SCHEDULE, dissector);
}
/* core scte35 splice_info_section dissector */
static int
dissect_scte35_avail_descriptor(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
gint offset = 0;
gint tvb_len;
/* Check length. */
tvb_len = (gint)tvb_reported_length(tvb);
if (tvb_len < 4)
return 0;
/* Show the field. */
proto_tree_add_item(tree, hf_descriptor_provider_avail_id, tvb, offset, 4, ENC_NA);
offset += 4;
return offset;
}
static int
dissect_scte35_dtmf_descriptor(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
gint offset = 0;
gint tvb_len, min_length = 2;
guint8 dtmf_count;
/* Check length. */
tvb_len = (gint)tvb_reported_length(tvb);
if (tvb_len < min_length)
return 0;
dtmf_count = tvb_get_bits8(tvb, (offset+1)* 8, 3);
/* Check length with DTMF string too. */
min_length += dtmf_count;
if (tvb_len < min_length)
return 0;
/* Describe header. */
proto_tree_add_item(tree, hf_descriptor_preroll, tvb, offset, 1, ENC_NA);
offset++;
proto_tree_add_item(tree, hf_descriptor_dtmf_count, tvb, offset, 1, ENC_NA);
proto_tree_add_item(tree, hf_descriptor_dtmf_reserved, tvb, offset, 1, ENC_NA);
offset++;
/* Show the DTMF string field. */
proto_tree_add_item(tree, hf_descriptor_dtmf, tvb,
offset, dtmf_count, ENC_NA | ENC_ASCII);
offset += dtmf_count;
return offset;
}
static int
dissect_scte35_component(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int idx) {
gint offset = 0;
proto_tree *subtree;
/* Create the subtree. */
proto_tree_add_subtree_format(tree, tvb, offset, 6, idx, &subtree, "Component %d", idx);
/* Display the component fields. */
proto_tree_add_item(subtree, hf_descriptor_component_tag, tvb,
offset, 1, ENC_NA);
offset++;
proto_tree_add_item(subtree, hf_descriptor_component_reserved, tvb,
offset, 1, ENC_NA);
proto_tree_add_item(subtree, hf_descriptor_component_pts_offset, tvb,
offset, 5, ENC_BIG_ENDIAN);
offset += 5;
return offset;
}
static int
dissect_scte35_segmentation_descriptor(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
gint offset = 0, dissected_length = 0, component;
guint8 cancel_indicator, psf, sdf, dnr, component_count, upid_length;
/* Parse the common header */
proto_tree_add_item(tree, hf_descriptor_event_id, tvb, offset, 4, ENC_BIG_ENDIAN);
offset += 4;
cancel_indicator = tvb_get_bits8(tvb, offset * 8, 1);
proto_tree_add_item(tree, hf_descriptor_cancel_indicator, tvb,
offset, 1, ENC_NA);
proto_tree_add_item(tree, hf_descriptor_reserved0, tvb,
offset, 1, ENC_NA);
offset++;
/* Parse fields for new segmentation events. */
if (!cancel_indicator) {
psf = tvb_get_bits8(tvb, offset * 8, 1);
sdf = tvb_get_bits8(tvb, offset * 8 + 1, 1);
dnr = tvb_get_bits8(tvb, offset * 8 + 2, 1);
proto_tree_add_item(tree, hf_descriptor_psf, tvb, offset, 1, ENC_NA);
proto_tree_add_item(tree, hf_descriptor_segmentation_duration_flag,
tvb, offset, 1, ENC_NA);
proto_tree_add_item(tree, hf_descriptor_delivery_not_restricted_flag,
tvb, offset, 1, ENC_NA);
/* Parse delivery flags */
if (dnr) {
proto_tree_add_item(tree, hf_descriptor_reserved1, tvb, offset, 1, ENC_NA);
} else {
proto_tree_add_item(tree, hf_descriptor_web_delivery_allowed_flag,
tvb, offset, 1, ENC_NA);
proto_tree_add_item(tree, hf_descriptor_no_regional_blackout_flag,
tvb, offset, 1, ENC_NA);
proto_tree_add_item(tree, hf_descriptor_archive_allow_flag,
tvb, offset, 1, ENC_NA);
proto_tree_add_item(tree, hf_descriptor_device_restrictions,
tvb, offset, 1, ENC_NA);
}
offset++;
/* Parse component segmentation offsets if not switched as a program. */
if (!psf) {
component_count = tvb_get_guint8(tvb, offset);
proto_tree_add_item(tree, hf_descriptor_component_count, tvb,
offset, 1, ENC_NA);
offset++;
/* Parse each component */
for (component = 0; component < component_count; ++component) {
dissected_length = dissect_scte35_component(
tvb_new_subset_length(tvb, offset, 6),
pinfo, tree, component);
/* Propagate errors. */
if (dissected_length < 1)
return dissected_length;
offset += dissected_length;
}
}
/* Parse segementation duration if present. */
if (sdf) {
proto_tree_add_item(tree, hf_descriptor_segmentation_duration, tvb,
offset, 5, ENC_BIG_ENDIAN);
offset += 5;
}
/* Parse UPID. */
proto_tree_add_item(tree, hf_descriptor_segmentation_upid_type, tvb,
offset, 1, ENC_NA);
offset++;
upid_length = tvb_get_guint8(tvb, offset);
proto_tree_add_item(tree, hf_descriptor_segmentation_upid_length, tvb,
offset, 1, ENC_NA);
offset++;
/* Only show non-empty UPIDs. */
if (upid_length) {
proto_tree_add_item(tree, hf_descriptor_segmentation_upid, tvb,
offset, upid_length, ENC_NA | ENC_ASCII);
offset += upid_length;
}
/* Parse Segment counts. */
proto_tree_add_item(tree, hf_descriptor_segmentation_type_id, tvb, offset, 1, ENC_NA);
offset++;
proto_tree_add_item(tree, hf_descriptor_segment_num, tvb, offset, 1, ENC_NA);
offset++;
proto_tree_add_item(tree, hf_descriptor_segments_expected, tvb, offset, 1, ENC_NA);
offset++;
}
return offset;
}
static int
dissect_scte35_splice_descriptor(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int idx)
{
proto_tree *subtree;
tvbuff_t *descriptor_tvb;
gint offset = 0, dissected_length = 0;
guint8 tag, length = 0;
/* Create the subtree header for the descriptor. */
tag = tvb_get_guint8(tvb, offset);
length = tvb_get_guint8(tvb, offset + 1);
proto_tree_add_subtree_format(
tree, tvb, offset, length + 2, idx, &subtree,
"Descriptor %d (0x%02x)", idx, tag);
/* Parse descriptor headers */
proto_tree_add_item(subtree, hf_splice_descriptor_tag, tvb, offset, 1, ENC_NA);
offset++;
proto_tree_add_item(subtree, hf_splice_descriptor_length, tvb, offset, 1, ENC_NA);
offset++;
proto_tree_add_item(subtree, hf_splice_descriptor_identifier, tvb,
offset, 4, ENC_BIG_ENDIAN);
offset += 4;
/* Parse the specific descriptor type. */
descriptor_tvb = tvb_new_subset_length(tvb, offset, length - 4);
switch (tag) {
case SCTE35_AVAIL_DESCRIPTOR:
dissected_length = dissect_scte35_avail_descriptor(descriptor_tvb, pinfo, subtree);
break;
case SCTE35_DTMF_DESCRIPTOR:
dissected_length = dissect_scte35_dtmf_descriptor(descriptor_tvb, pinfo, subtree);
break;
case SCTE35_SEGMENTATION_DESCRIPTOR:
dissected_length = dissect_scte35_segmentation_descriptor(descriptor_tvb, pinfo, subtree);
break;
default:
/* Just trust the descriptor_length field. */
dissected_length = length - 4;
}
/* Propagate errors. */
if (dissected_length < 1)
return dissected_length;
offset += dissected_length;
return offset;
}
static int
dissect_scte35_splice_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
gint tvb_len, min_length = SCTE35_SI_MIN_LEN, dissected_length = 0;
guint8 table_id, encrypted_packet, command_type;
guint16 command_length, descriptor_loop_length, i;
proto_item *ti;
proto_tree *splice_info_tree;
gint offset = 0, descriptor_offset = 0;
tvbuff_t *command_tvb;
const gint *section_flags[] = {
&hf_section_syntax_indicator,
&hf_private_indicator,
&hf_reserved,
&hf_section_length,
NULL
};
const gint *encrypt_flags[] = {
&hf_encrypted_packet,
&hf_encryption_algorithm,
&hf_pts_adjustment,
NULL
};
tvb_len = (gint)tvb_reported_length(tvb);
if (tvb_len < min_length)
return 0;
/* Pre-fetch a few fields in the message. */
table_id = tvb_get_guint8(tvb, offset);
encrypted_packet = tvb_get_guint8(tvb, offset + 4) & 0x80;
command_type = tvb_get_guint8(tvb, offset + 13);
command_length = tvb_get_ntohs(tvb, offset + 11) & 0xFFF;
/* Check for excessive length before indexing past the command. */
min_length += command_length;
if (tvb_len < min_length)
return 0;
/* Determine length of descriptors. */
descriptor_loop_length = tvb_get_ntohs(tvb, 14 + command_length);
min_length += descriptor_loop_length;
if (tvb_len < min_length)
return 0;
/* Check for excessive length before parsing the remainder of the packet. */
if (encrypted_packet)
min_length += 4;
if (tvb_len < min_length)
return 0;
/* Set up headers in the packet list */
col_set_str(pinfo->cinfo, COL_PROTOCOL, "SCTE-35");
col_add_fstr(pinfo->cinfo, COL_INFO, "Table 0x%02x", table_id);
/* Create the protocol header. */
ti = proto_tree_add_item(tree, proto_scte35, tvb, 0, -1, ENC_NA);
splice_info_tree = proto_item_add_subtree(ti, ett_scte35_splice_info_section);
/* Explain the root fields. */
proto_tree_add_item(splice_info_tree, hf_table_id, tvb, offset, 1, ENC_BIG_ENDIAN);
offset++;
proto_tree_add_bitmask_list(splice_info_tree, tvb, offset, 2, section_flags, ENC_BIG_ENDIAN);
offset += 2;
proto_tree_add_item(splice_info_tree, hf_protocol_version, tvb, offset, 1, ENC_BIG_ENDIAN);
offset++;
/* 7 bits of flags, 33 bits of PTS */
proto_tree_add_bitmask_list(splice_info_tree, tvb, offset, 5, encrypt_flags, ENC_BIG_ENDIAN);
offset += 5;
proto_tree_add_item(splice_info_tree, hf_cw_index, tvb, offset, 1, ENC_BIG_ENDIAN);
offset++;
/* Two twelve-bit fields */
proto_tree_add_item(splice_info_tree, hf_tier, tvb, offset, 2, ENC_BIG_ENDIAN);
offset++;
proto_tree_add_item(splice_info_tree, hf_splice_command_length, tvb, offset, 2, ENC_BIG_ENDIAN);
offset += 2;
proto_tree_add_item(splice_info_tree, hf_splice_command_type, tvb, offset, 1, ENC_BIG_ENDIAN);
offset++;
/* Extract the splice command payload for later use. */
command_tvb = tvb_new_subset_length(tvb, offset, command_length);
offset += command_length;
/* Process the descriptor loop. */
proto_tree_add_item(splice_info_tree, hf_descriptor_loop_length, tvb, offset, 2, ENC_BIG_ENDIAN);
offset += 2;
/* Explain each descriptor. */
for (i = 0, descriptor_offset = offset;
descriptor_offset < offset + descriptor_loop_length;
++i) {
dissected_length = dissect_scte35_splice_descriptor( tvb_new_subset_remaining(tvb, descriptor_offset),
pinfo, splice_info_tree, i);
/* Escalate failure. */
if (dissected_length < 1)
return offset;
descriptor_offset += dissected_length;
}
offset += descriptor_loop_length;
/* Explain the packet footer. */
if (encrypted_packet) {
proto_tree_add_item(splice_info_tree, hf_e_crc32, tvb, offset, 4, ENC_BIG_ENDIAN);
offset += 4;
}
proto_tree_add_item(splice_info_tree, hf_crc32, tvb, offset, 4, ENC_BIG_ENDIAN);
offset += 4;
/* We've reached the end. Run a child dissector for the splice command. */
dissector_try_uint_new(scte35_cmd_dissector_table, command_type, command_tvb, pinfo, tree,
FALSE, NULL);
return offset;
}
void
proto_register_scte35(void)
{
static gint *ett[] = {
&ett_scte35_splice_info_section,
};
static hf_register_info hf[] = {
/* MPEG Section Table Headers. Field members taken from mpeg-sect.c. */
{&hf_table_id,
{"Table ID", "scte35.tid", FT_UINT8, BASE_HEX,
NULL, 0, NULL, HFILL}},
{&hf_section_syntax_indicator,
{"Section Syntax Identifier", "scte35.syntax_indicator", FT_BOOLEAN,
16, TFS(&tfs_section_syntax_indicator), 0x8000, NULL, HFILL }},
{&hf_private_indicator,
{"Private Indicator", "scte35.private", FT_BOOLEAN,
16, TFS(&tfs_private_indicator), 0x4000, NULL, HFILL }},
{&hf_reserved,
{"Reserved", "scte35.reserved", FT_UINT16, BASE_HEX,
NULL, 0x3000, NULL, HFILL }},
{&hf_section_length,
{"Section length", "scte35.len", FT_UINT16, BASE_DEC,
NULL, 0x0FFF, NULL, HFILL}},
/* SCTE35-specific headers */
{&hf_protocol_version,
{"Protocol Version", "scte35.protocol_version", FT_UINT8, BASE_DEC,
NULL, 0, NULL, HFILL}},
{&hf_encrypted_packet,
{"Encrypted Packet", "scte35.encrypted_packet", FT_BOOLEAN, 40,
TFS(&tfs_encrypted_packet), G_GUINT64_CONSTANT(0x8000000000), NULL, HFILL}},
{&hf_encryption_algorithm,
{"Encryption Algorithm", "scte35.encryption_algorithm", FT_UINT40,
BASE_HEX | BASE_RANGE_STRING, RVALS(rv_encryption_algorithm),
G_GUINT64_CONSTANT(0x7E00000000), NULL, HFILL}},
{&hf_pts_adjustment,
{"PTS Adjustment", "scte35.pts_adjustment", FT_UINT40, BASE_DEC,
NULL, G_GUINT64_CONSTANT(0x1FFFFFFFF), NULL, HFILL}},
{&hf_cw_index,
{"Control Word Index", "scte35.cw_index", FT_UINT8, BASE_HEX,
NULL, 0, NULL, HFILL}},
{&hf_tier,
{"Authorisation Tier", "scte35.tier", FT_UINT16, BASE_DEC,
NULL, 0xFFF0, NULL, HFILL}},
{&hf_splice_command_length,
{"Command Length", "scte35.splice_command_length", FT_UINT16, BASE_DEC,
NULL, 0xFFF, NULL, HFILL}},
{&hf_splice_command_type,
{"Command Type", "scte35.splice_command_type", FT_UINT8,
BASE_HEX | BASE_RANGE_STRING, RVALS(rv_splice_command_type),
0, NULL, HFILL}},
/* Splice command payload goes here via the dissector table. */
/* Descriptor loop header. */
{&hf_descriptor_loop_length,
{"Descriptor Loop Length", "scte35.desc_len", FT_UINT16, BASE_DEC,
NULL, 0, NULL, HFILL}},
/* Descriptor loop entries. */
{&hf_splice_descriptor_tag,
{"Tag", "scte35.splice_descriptor.tag", FT_UINT8,
BASE_HEX | BASE_RANGE_STRING, RVALS(rv_splice_descriptor_tag),
0, NULL, HFILL}},
{&hf_splice_descriptor_length,
{"Length", "scte35.splice_descriptor.length", FT_UINT8, BASE_DEC,
NULL, 0, NULL, HFILL}},
{&hf_splice_descriptor_identifier,
{"Descriptor ID", "scte35.splice_descriptor.identifier", FT_UINT32,
BASE_HEX, NULL, 0, NULL, HFILL}},
/* avail_descriptor */
{&hf_descriptor_provider_avail_id,
{"Provider Avail ID", "scte35.splice_descriptor.provider_avail_id",
FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL}},
/* dtmf_descriptor */
{&hf_descriptor_preroll,
{"Preroll", "scte35.splice_descriptor.preroll",
FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL}},
{&hf_descriptor_dtmf_count,
{"DTMF Count", "scte35.splice_descriptor.dtmf_count",
FT_UINT8, BASE_DEC, NULL, 0xE0, NULL, HFILL}},
{&hf_descriptor_dtmf_reserved,
{"DTMF Reserved", "scte35.splice_descriptor.dtmf_reserved",
FT_UINT8, BASE_HEX, NULL, 0x1F, NULL, HFILL}},
{&hf_descriptor_dtmf,
{"DTMF", "scte35.splice_descriptor.dtmf",
FT_STRING, 0, NULL, 0, NULL, HFILL}},
/* segmentation_descriptor */
{&hf_descriptor_event_id,
{"Segmentation Event ID", "scte35.splice_descriptor.event_id",
FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL}},
{&hf_descriptor_cancel_indicator,
{"Cancel Indicator", "scte35.splice_descriptor.cancel_indicator",
FT_BOOLEAN, 8, TFS(&tfs_descriptor_cancel_indicator), 0x80, NULL, HFILL}},
{&hf_descriptor_reserved0,
{"Reserved", "scte35.splice_descriptor.reserved0",
FT_UINT8, BASE_HEX, NULL, 0x7F, NULL, HFILL}},
{&hf_descriptor_psf,
{"Program Segmentation Flag", "scte35.splice_descriptor.psf",
FT_BOOLEAN, 8, TFS(&tfs_descriptor_psf), 0x80, NULL, HFILL}},
{&hf_descriptor_segmentation_duration_flag,
{"Segmentation Duration Flag", "scte35.splice_descriptor.sdf",
FT_BOOLEAN, 8, TFS(&tfs_descriptor_sdf), 0x40, NULL, HFILL}},
{&hf_descriptor_delivery_not_restricted_flag,
{"Delivery not Restricted", "scte35.splice_descriptor.dnr",
FT_BOOLEAN, 8, TFS(&tfs_descriptor_dnr), 0x20, NULL, HFILL}},
{&hf_descriptor_web_delivery_allowed_flag,
{"Web Delivery Allowed", "scte35.splice_descriptor.web_delivery_allowed",
FT_BOOLEAN, 8, TFS(&tfs_descriptor_web), 0x10, NULL, HFILL}},
{&hf_descriptor_no_regional_blackout_flag,
{"No Regional Blackout", "scte35.splice_descriptor.no_regional_blackout",
FT_BOOLEAN, 8, TFS(&tfs_descriptor_blackout), 0x08, NULL, HFILL}},
{&hf_descriptor_archive_allow_flag,
{"Archive Allowed", "scte35.splice_descriptor.archive_allowed",
FT_BOOLEAN, 8, TFS(&tfs_descriptor_archive), 0x04, NULL, HFILL}},
{&hf_descriptor_device_restrictions,
{"Device Restrictions", "scte35.splice_descriptor.device_restrictions",
FT_UINT8, BASE_HEX | BASE_RANGE_STRING,
RVALS(scte35_device_restrictions), 0x03, NULL, HFILL}},
{&hf_descriptor_reserved1,
{"Reserved", "scte35.splice_descriptor.reserved1",
FT_UINT8, BASE_HEX, NULL, 0x1F, NULL, HFILL}},
{&hf_descriptor_component_count,
{"Component Count", "scte35.splice_descriptor.component_count",
FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL}},
{&hf_descriptor_component_tag,
{"Component Tag", "scte35.splice_descriptor.component.tag",
FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL}},
{&hf_descriptor_component_reserved,
{"Reserved", "scte35.splice_descriptor.component.reserved",
FT_UINT8, BASE_HEX, NULL, 0xFE, NULL, HFILL}},
{&hf_descriptor_component_pts_offset,
{"PTS Offset", "scte35.splice_descriptor.component.pts_offset",
FT_UINT64, BASE_DEC, NULL, 0x1FFFFFFFF, NULL, HFILL}},
{&hf_descriptor_segmentation_duration,
{"Segmentation Duration", "scte35.splice_descriptor.segmentation_duration",
FT_UINT64, BASE_DEC, NULL, 0xFFFFFFFFFF, NULL, HFILL}},
{&hf_descriptor_segmentation_upid_type,
{"UPID Type", "scte35.splice_descriptor.upid_type",
FT_UINT8, BASE_HEX | BASE_RANGE_STRING,
RVALS(scte35_segmentation_upid_type), 0, NULL, HFILL}},
{&hf_descriptor_segmentation_upid_length,
{"UPID Length", "scte35.splice_descriptor.upid_length",
FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL}},
{&hf_descriptor_segmentation_upid,
{"UPID", "scte35.splice_descriptor.upid",
FT_STRING, 0, NULL, 0, NULL, HFILL}},
{&hf_descriptor_segmentation_type_id,
{"Segmentation Type", "scte35.splice_descriptor.segmentation_type_id",
FT_UINT8, BASE_HEX | BASE_RANGE_STRING,
RVALS(scte35_segmentation_type_id), 0, NULL, HFILL}},
{&hf_descriptor_segment_num,
{"Segment Number", "scte35.splice_descriptor.segment_num",
FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL}},
{&hf_descriptor_segments_expected,
{"Segments Expected", "scte35.splice_descriptor.segments_expected",
FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL}},
/* Optional alignment padding, encrypted CRC32 suffix. */
{&hf_e_crc32,
{"Encrypted CRC32", "scte35.ecrc32", FT_UINT32, BASE_HEX,
NULL, 0, NULL, HFILL}},
/* MPEG Section table CRC suffix */
{&hf_crc32,
{"CRC32", "scte35.crc", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL}},
};
/* Allocate a protocol number. */
proto_scte35 = proto_register_protocol(
"SCTE-35 Splice Information", /* name */
"SCTE 35", /* short name */
"scte35" /* abbreviation */
);
scte35_handle = register_dissector("scte35", dissect_scte35_splice_info, proto_scte35);
/* Register groups and fields. */
proto_register_subtree_array(ett, array_length(ett));
proto_register_field_array(proto_scte35, hf, array_length(hf));
/* Allow other protocols to discriminate against the splice command type
* to further dissect the payload.
*/
scte35_cmd_dissector_table = register_dissector_table(
"scte35.splice_command_type", "SCTE-35 Command", proto_scte35, FT_UINT8,
BASE_HEX);
}
void
proto_reg_handoff_scte35(void)
{
/* Invoke the splice_info_section parser for a section table with ID 0xFC */
dissector_add_uint("mpeg_sect.tid", SCTE35_TABLE_ID, scte35_handle);
}
/*
* Editor modelines - http://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/