304 changes: 300 additions & 4 deletions mythtv/libs/libmythtv/mpeg/mpegtables.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ class StreamID
MPEG2IPMP = 0x1a, ///< ISO 13818-10 Digital Restrictions Mangment
MPEG2IPMP2 = 0x7f, ///< ISO 13818-10 Digital Restrictions Mangment

Splice = 0x86, ///< ANSI/SCTE 35 2007

// special id's, not actually ID's but can be used in FindPIDs
AnyMask = 0xFFFF0000,
AnyVideo = 0xFFFF0001,
Expand Down Expand Up @@ -290,6 +292,8 @@ class TableID
CEA = 0xD8, // Cable Emergency Alert (18 2002)
ADET = 0xD9, // Aggregate Data Event Table (80 2002)

SITscte = 0xFC, // SCTE 35 Splice Info Table (Cueing messages)

// ATSC Conditional Access (A/70)
ECM0 = 0x80,
ECM1 = 0x81,
Expand Down Expand Up @@ -408,9 +412,9 @@ class PSIPTable : public PESPacket
// last_section_number 8 7.0 56
uint LastSection(void) const { return pesdata()[7]; }

// this is only for real ATSC PSIP tables, not similar MPEG2 tables
// Protocol Version for ATSC PSIP tables
// protocol_version 8 8.0 64 should always be 0 for now
uint ProtocolVersion(void) const { return pesdata()[8]; }
uint ATSCProtocolVersion(void) const { return pesdata()[8]; }

// PSIP_table_data x 8.0 72 (incl. protocolVersion)
const unsigned char* psipdata(void) const
Expand All @@ -435,8 +439,8 @@ class PSIPTable : public PESPacket
void SetSection(uint num) { pesdata()[6] = num; }
void SetLastSection(uint num) { pesdata()[7] = num; }

// only for real ATSC PSIP tables, not similar MPEG2 tables
void SetProtocolVersion(int ver) { pesdata()[8] = ver; }
// Only for real ATSC PSIP tables.
void SetATSCProtocolVersion(int ver) { pesdata()[8] = ver; }

bool HasCRC(void) const;

Expand Down Expand Up @@ -721,6 +725,298 @@ class ConditionalAccessTable : public PSIPTable
// CRC_32 32 rpchof
};


class SpliceTimeView
{
public:
SpliceTimeView(const unsigned char *data) : _data(data) { }
// time_specified_flag 1 0.0
bool IsTimeSpecified(void) const { return _data[0] & 0x80; }
// if (time_specified_flag == 1)
// reserved 6 0.1
// pts_time 33 0.6
uint64_t PTSTime(void) const
{
return ((uint64_t(_data[0] & 0x1) << 32) |
(uint64_t(_data[1]) << 24) |
(uint64_t(_data[2]) << 16) |
(uint64_t(_data[3]) << 8) |
(uint64_t(_data[4])));
}
// else
// reserved 7 0.1
// }
uint size(void) const { return IsTimeSpecified() ? 1 : 5; }
private:
const unsigned char *_data;
};

class SpliceScheduleView
{
public:
SpliceScheduleView(const vector<const unsigned char*> &ptrs0,
const vector<const unsigned char*> &ptrs1) :
_ptrs0(ptrs0), _ptrs1(ptrs1)
{
}
// splice_count 8 14.0
uint SpliceCount(void) const { return min(_ptrs0.size(), _ptrs1.size()); }

// splice_event_id 32 0.0 + _ptrs0[i]
uint SpliceEventID(uint i) const
{
return ((_ptrs0[i][0] << 24) | (_ptrs0[i][1] << 16) |
(_ptrs0[i][2] << 8) | (_ptrs0[i][3]));
}
// splice_event_cancel_indicator 1 4.0 + _ptrs0[i]
// reserved 7 4.1 + _ptrs0[i]
// if (splice_event_cancel_indicator == ‘0’) {
// out_of_network_indicator 1 5.0 + _ptrs0[i]
// program_splice_flag 1 5.1 + _ptrs0[i]
// duration_flag 1 5.2 + _ptrs0[i]
// reserved 5 5.3 + _ptrs0[i]
// if (program_splice_flag == ‘1’)
// utc_splice_time 32 6.0 + _ptrs0[i]
// else {
// component_count 8 6.0 + _ptrs0[i]
// for(j = 0; j < component_count; j++) {
// component_tag 8 7.0 + _ptrs0[i]+j*5
// utc_splice_time 32 8.0 + _ptrs0[i]+j*5
// }
// }
// if (duration_flag) {
// auto_return 1 0.0 + _ptrs1[i]
// reserved 6 0.1 + _ptrs1[i]
// duration 33 0.7 + _ptrs1[i]
// }
// unique_program_id 16 0.0 + _ptrs1[i] + (duration_flag)?5:0
// avail_num 8 2.0 + _ptrs1[i] + (duration_flag)?5:0
// avails_expected 8 3.0 + _ptrs1[i] + (duration_flag)?5:0
// }

private:
vector<const unsigned char*> _ptrs0;
vector<const unsigned char*> _ptrs1;
};

class SpliceInsertView
{
public:
SpliceInsertView(const vector<const unsigned char*> &ptrs0,
const vector<const unsigned char*> &ptrs1) :
_ptrs0(ptrs0), _ptrs1(ptrs1)
{
}

// splice_event_id 32 0.0 + _ptrs1[0]
uint SpliceEventID(void) const
{
return ((_ptrs1[0][0] << 24) | (_ptrs1[0][1] << 16) |
(_ptrs1[0][2] << 8) | (_ptrs1[0][3]));
}
// splice_event_cancel 1 4.0 + _ptrs1[0]
bool IsSpliceEventCancel(void) const { return _ptrs1[0][4] & 0x80; }
// reserved 7 4.1 + _ptrs1[0]
// if (splice_event_cancel_indicator == 0) {
// out_of_network_flag 1 5.0 + _ptrs1[0]
// program_splice_flag 1 5.1 + _ptrs1[0]
// duration_flag 1 5.2 + _ptrs1[0]
// splice_immediate_flag 1 5.3 + _ptrs1[0]
// reserved 4 5.4 + _ptrs1[0]
// if ((program_splice_flag == 1) && (splice_immediate_flag == ‘0’))
// splice_time() 8-38 6.0 + _ptrs1[0]
// if (program_splice_flag == 0) {
// component_count 8 6.0 + _ptrs1[0]
// for (i = 0; i < component_count; i++) {
// component_tag 8 0.0 + _ptrs0[i]
// if (splice_immediate_flag == ‘0’)
// splice_time() 8-38 1.0 + _ptrs0[i]
// }
// }
// if (duration_flag == ‘1’)
// auto_return 1 0.0 + _ptrs1[1]
// reserved 6 0.1 + _ptrs1[1]
// duration 33 0.7 + _ptrs1[1]
// unique_program_id 16 0.0 + _ptrs1[2]
// avail_num 8 2.0 + _ptrs1[2]
// avails_expected 8 3.0 + _ptrs1[2]
// }

private:
vector<const unsigned char*> _ptrs0;
vector<const unsigned char*> _ptrs1;
};

class SpliceInformationTable : public PSIPTable
{
public:
SpliceInformationTable(const SpliceInformationTable &table) :
PSIPTable(table), _epilog(NULL)
{
assert(TableID::SITscte == TableID());
Parse();
}
SpliceInformationTable(const PSIPTable &table) :
PSIPTable(table), _epilog(NULL)
{
assert(TableID::SITscte == TableID());
Parse();
}
~SpliceInformationTable() { ; }

// ANCE/SCTE 35 2007
// Name bits loc expected value
// table_id 8 0.0 0xFC
// section_syntax_indicator 1 1.0 1
// private_indicator 1 1.1 0
// reserved 2 1.2 3
// section_length 12 1.4
// ^^^ All above this line provided by PSIPTable
// protocol_version 8 3.0 0
uint SpliceProtocolVersion(void) const { return pesdata()[3]; }
void SetSpliceProtocolVersion(uint ver) { pesdata()[3] = ver; }
// encrypted_packet 1 4.0
bool IsEncryptedPacket(void) const { return pesdata()[4] & 0x80; }
void SetEncryptedPacket(bool val)
{
pesdata()[4] = (pesdata()[4] & ~0x80) | ((val) ? 0x80 : 0);
}
// encryption_algorithm 6 4.1
enum
{
kNoEncryption = 0,
kECB = 1, // DES - ECB mode, FIPS PUB 81 (8 byte blocks)
kCBC = 2, // DES - CBC mode, FIPS PUB 81 (8 byte blocks)
k3DES = 3, // 3 DES - TDEA, FIPS PUB 46-3 (8 byte blocks)
// values 4-31 are reserved for future extension
// values 32-63 are user private
};
uint EncryptionAlgorithm(void) const { return (pesdata()[4] >> 1) & 0x3f; }
QString EncryptionAlgorithmString(void) const;
void SetEncryptionAlgorithm(uint val)
{
pesdata()[4] &= 0x81;
pesdata()[4] |= ((val&0x3f) << 1);
}
// pts_adjustment 33 4.7
uint64_t PTSAdjustment(void) const
{
return ((uint64_t(pesdata()[4] & 0x1) << 32) |
(uint64_t(pesdata()[5]) << 24) |
(uint64_t(pesdata()[6]) << 16) |
(uint64_t(pesdata()[7]) << 8) |
(uint64_t(pesdata()[8])));
}
void SetPTSAdjustment(uint64_t val)
{
pesdata()[4] &= ~0x1;
pesdata()[4] |= (val>>32) & 0x1;
pesdata()[5] = ((val>>24) & 0xff);
pesdata()[6] = ((val>>16) & 0xff);
pesdata()[7] = ((val>>8 ) & 0xff);
pesdata()[8] = ((val ) & 0xff);
}
// cw_index (enc key) 8 9.0
uint CodeWordIndex(void) const { return pesdata()[9]; }
void SetCodeWordIndex(uint val) { pesdata()[9] = val; }
// reserved 12 10.0
// splice_command_length 12 11.4
uint SpliceCommandLength(void) const
{
return ((pesdata()[11] & 0xf) << 8) | pesdata()[12];
}
void SetSpliceCommandLength(uint len)
{
pesdata()[11] &= ~0xf;
pesdata()[11] |= (len >> 8) & 0xf;
pesdata()[12] = len & 0xff;
}
// splice_command_type 8 13.0
enum {
kSCTNull = 0x00,
kSCTReserved0 = 0x01,
kSCTReserved1 = 0x02,
kSCTReserved2 = 0x03,
kSCTSpliceSchedule = 0x04,
kSCTSpliceInsert = 0x05,
kSCTTimeSignal = 0x06,
kSCTBandwidthReservation = 0x07,
// 0x08-0xfe reserved
kSCTPrivateCommand = 0xff,
};
uint SpliceCommandType(void) const { return pesdata()[13]; }
QString SpliceCommandTypeString(void) const;
void SetSpliceCommandType(uint type) { pesdata()[13] = type & 0xff; }

// ALL BELOW THIS LINE OTHER THAN CRC_32 ARE ENCRYPTED IF FLAG SET

//////////// SPLICE NULL ////////////

// nothing here, info in descriptors

//////////// SPLICE SCHEDULE ////////////

// if (splice_command_type == 0x04) splice_schedule()
SpliceScheduleView SpliceSchedule(void) const
{ return SpliceScheduleView(_ptrs0, _ptrs1); }

//////////// SPLICE INSERT ////////////

// if (splice_command_type == 0x05) splice_insert()
SpliceInsertView SpliceInsert(void) const
{ return SpliceInsertView(_ptrs0, _ptrs1); }

//////////// TIME SIGNAL ////////////

// if (splice_command_type == 0x06) splice_time()
SpliceTimeView TimeSignal(void) const
{ return SpliceTimeView(pesdata()+14); }

//////////// BANDWIDTH RESERVATION ////////////

// nothing here, info in descriptors

//////////// PRIVATE COMMAND ////////////

// if (splice_command_type == 0xff) private_command()
// identifier 32 14.0
// for (i = 0; i < N; i++)
// private_byte 8 ??.0
//

//////////////////////////////////////////////////////////////
// NOTE: Aside from CRC's we can not interpret things below
// this comment with private or reserved commands.

// descriptor_loop_length 16 0.0 + _epilog
uint SpliceDescriptorsLength(uint i) const
{
return (_epilog[0] << 8) | _epilog[1];
}

// for (i = 0; i < ? ; i++)
// splice_descriptor() ?? ??.?
const unsigned char *SpliceDescriptors(void) const
{
return (_epilog) ? _epilog + 2 : NULL;
}
// for (i = 0; i < ?; i++)
// alignment_stuffing 8 ??.0
// if (encrypted_packet())
// E_CRC_32 32 ??.0
// CRC_32 32 ??.0

SpliceInformationTable *GetDecrypted(const QString &codeWord) const;
bool Parse(void);
QString toString(void) const;
QString toStringXML(void) const;

private:
vector<const unsigned char*> _ptrs0;
vector<const unsigned char*> _ptrs1;
const unsigned char *_epilog;
};

/** \class AdaptationFieldControl
* \brief AdaptationFieldControl is used to transmit various important
* stream attributes.
Expand Down
214 changes: 214 additions & 0 deletions mythtv/libs/libmythtv/mpeg/splicedescriptors.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
// -*- Mode: c++ -*-
/**
* ANSI/SCTE 35 splice descriptor implementation
* Copyright (c) 2011, Digital Nirvana, Inc.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

#ifndef _SPLICE_DESCRIPTOR_H_
#define _SPLICE_DESCRIPTOR_H_

#include "splicedescriptors.h"

desc_list_t SpliceDescriptor::Parse(
const unsigned char *data, uint len)
{
desc_list_t tmp;
uint off = 0;
while (off < len)
{
tmp.push_back(data+off);
SpliceDescriptor desc(data+off, len-off);
if (!desc.IsValid())
{
tmp.pop_back();
break;
}
off += desc.size();
}
return tmp;
}

desc_list_t SpliceDescriptor::ParseAndExclude(
const unsigned char *data, uint len, int excluded_descid)
{
desc_list_t tmp;
uint off = 0;
while (off < len)
{
if ((data+off)[0] != excluded_descid)
tmp.push_back(data+off);
SpliceDescriptor desc(data+off, len-off);
if (!desc.IsValid())
{
if ((data+off)[0] != excluded_descid)
tmp.pop_back();
break;
}
off += desc.size();
}
return tmp;
}

desc_list_t SpliceDescriptor::ParseOnlyInclude(
const unsigned char *data, uint len, int excluded_descid)
{
desc_list_t tmp;
uint off = 0;
while (off < len)
{
if ((data+off)[0] == excluded_descid)
tmp.push_back(data+off);
SpliceDescriptor desc(data+off, len-off);
if (!desc.IsValid())
{
if ((data+off)[0] == excluded_descid)
tmp.pop_back();
break;
}
off += desc.size();
off += desc.DescriptorLength() + 2;
}
return tmp;
}

const unsigned char *SpliceDescriptor::Find(
const desc_list_t &parsed, uint desc_tag)
{
desc_list_t::const_iterator it = parsed.begin();
for (; it != parsed.end(); ++it)
{
if ((*it)[0] == desc_tag)
return *it;
}
return NULL;
}

desc_list_t SpliceDescriptor::FindAll(const desc_list_t &parsed, uint desc_tag)
{
desc_list_t tmp;
desc_list_t::const_iterator it = parsed.begin();
for (; it != parsed.end(); ++it)
{
if ((*it)[0] == desc_tag)
tmp.push_back(*it);
}
return tmp;
}

QString SpliceDescriptor::DescriptorTagString(void) const
{
switch (DescriptorTag())
{
case SpliceDescriptorID::avail:
return QString("Avail");
case SpliceDescriptorID::dtmf:
return QString("DTMF");
case SpliceDescriptorID::segmentation:
return QString("Segmentation");
default:
return QString("Unknown(%1)").arg(DescriptorTag());
}
}

QString SpliceDescriptor::toString(void) const
{
QString str;

if (SpliceDescriptorID::avail == DescriptorTag())
str = AvailDescriptor(_data).toString();
else if (SpliceDescriptorID::dtmf == DescriptorTag())
str = DTMFDescriptor(_data).toString();
else if (SpliceDescriptorID::segmentation == DescriptorTag())
str = SegmentationDescriptor(_data).toString();
else
{
str.append(QString("%1 Splice Descriptor (0x%2)")
.arg(DescriptorTagString())
.arg(int(DescriptorTag()), 0, 16));
str.append(QString(" length(%1)").arg(int(DescriptorLength())));
for (uint i=0; i<DescriptorLength(); i++)
str.append(QString(" 0x%1").arg(int(_data[i+2]), 0, 16));
}

return str;
}

static inline QString indent(uint level)
{
QString ret;
for (uint i = 0; i < level; i++)
ret += " ";
return ret;
}

/// Returns XML representation of string the TS Reader XML format.
/// When possible matching http://www.tsreader.com/tsreader/text-export.html
QString SpliceDescriptor::toStringXML(uint level) const
{
QString indent_0 = indent(level);
QString indent_1 = indent(level+1);
QString str;

str += indent_0 + "<DESCRIPTOR namespace=\"splice\">\n";
str += indent_1 + QString("<TAG>0x%1</TAG>\n")
.arg(DescriptorTag(),2,16,QChar('0'));
str += indent_1 + QString("<DESCRIPTION>%1</DESCRIPTION>\n")
.arg(DescriptorTagString(),0,16);

str += indent_1 + "<DATA>";
for (uint i = 0; i < DescriptorLength(); i++)
str += QString("0x%1 ").arg(_data[i+2],2,16,QChar('0'));
str = str.trimmed();
str += "</DATA>\n";

str += indent_1 + "<DECODED>" + toString() + "</DECODED>";

str += indent_0 + "</DESCRIPTOR>";

return str;
}

bool DTMFDescriptor::IsParsible(const unsigned char *data, uint safe_bytes)
{
if (safe_bytes < 8)
return false;
if (data[0] != SpliceDescriptorID::dtmf)
return false;
uint len = data[1];
if (len + 2 > safe_bytes)
return false;
if (data[2] != 'C' || data[3] != 'U' ||
data[4] != 'E' || data[5] != 'I')
return false;
return len == (6 + uint(data[7]>>5));
}

bool SegmentationDescriptor::Parse(void)
{
_ptrs[0] = _data + (IsProgramSegmentation() ? 12 : 13 + ComponentCount() * 6);
_ptrs[1] = _ptrs[0] + (HasSegmentationDuration() ? 5 : 0);
_ptrs[2] = _ptrs[1] + 2 + SegmentationUPIDLength();
return true;
}

QString SegmentationDescriptor::toString() const
{
// TODO
return QString("Segmentation: id(%1)").arg(SegmentationEventIdString());
}

#endif // _SPLICE_DESCRIPTOR_H_
284 changes: 284 additions & 0 deletions mythtv/libs/libmythtv/mpeg/splicedescriptors.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
// -*- Mode: c++ -*-
/**
* ANSI/SCTE 35 splice descriptor implementation
* Copyright (c) 2011, Digital Nirvana, Inc.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

#include <stdint.h>

#include <QByteArray>

#include "mpegdescriptors.h"

// These descriptors are not registered descriptors, but private
// descriptors that should only be seen on an SpliceInformationTable

class SpliceDescriptorID
{
// ANSI SCTE 35 2007
public:
enum
{
avail = 0x00,
dtmf = 0x01,
segmentation = 0x02,
};
};

class SpliceDescriptor
{
public:
operator const unsigned char*(void) const { return _data; }

SpliceDescriptor(const unsigned char *data, int len) : _data(data)
{
if ((len < 2) || (int(DescriptorLength()) + 2) > len)
_data = NULL;
else if (!Parse())
_data = NULL;
}
SpliceDescriptor(const unsigned char *data,
int len, uint tag) : _data(data)
{
if ((len < 2) || (int(DescriptorLength()) + 2) > len)
_data = NULL;
else if (DescriptorTag() != tag)
_data = NULL;
else if (!Parse())
_data = NULL;
}
virtual ~SpliceDescriptor(void) {}

bool IsValid(void) const { return _data; }
uint size(void) const { return DescriptorLength() + 2; }

// Name bits loc expected value
// splice_descriptor_tag 8 0.0 0x01
uint DescriptorTag(void) const { return _data[0]; }
QString DescriptorTagString(void) const;
// descriptor_length 8 1.0
uint DescriptorLength(void) const { return _data[1]; }
// identifier 32 2.0 0x43554549 "CUEI"
uint Identifier(void) const
{
return (_data[2]<<24) | (_data[3]<<16) | (_data[4]<<8) | _data[5];
}
QString IdentifierString(void) const
{
return QString(QChar(_data[2])) + QChar(_data[3]) +
QChar(_data[4]) + QChar(_data[5]);
}

virtual QString toString(void) const;
virtual QString toStringXML(uint indent_level) const;

static desc_list_t Parse(const unsigned char *data, uint len);
static desc_list_t ParseAndExclude(const unsigned char *data, uint len,
int descriptorid);
static desc_list_t ParseOnlyInclude(const unsigned char *data, uint len,
int descriptorid);

static const unsigned char *Find(const desc_list_t &parsed, uint desc_tag);
static desc_list_t FindAll(const desc_list_t &parsed, uint desc_tag);

protected:
virtual bool Parse(void) { return true; }

const unsigned char *_data;
};

class AvailDescriptor : SpliceDescriptor
{
public:
AvailDescriptor(const unsigned char *data, int len = 300) :
SpliceDescriptor(data, len, SpliceDescriptorID::segmentation) { }
// Name bits loc expected value
// splice_descriptor_tag 8 0.0 0x00
// descriptor_length 8 1.0 0x08
// identifier 32 2.0 0x43554549 "CUEI"
// provider_avail_id 32 6.0
uint ProviderAvailId(void) const
{
return (_data[2]<<24) | (_data[3]<<16) | (_data[4]<<8) | _data[5];
}
QString ProviderAvailIdString(void) const
{
return QString(QChar(_data[6])) + QChar(_data[7]) +
QChar(_data[8]) + QChar(_data[9]);
}

virtual QString toString(void) const
{
return QString("Splice Avail: id(%1)").arg(ProviderAvailId());
}
};

class DTMFDescriptor : SpliceDescriptor
{
public:
DTMFDescriptor(const unsigned char *data, int len = 300) :
SpliceDescriptor(data, len, SpliceDescriptorID::dtmf) { }

// Name bits loc expected value
// splice_descriptor_tag 8 0.0 0x01
// descriptor_length 8 1.0
// identifier 32 2.0 0x43554549 "CUEI"
// preroll 8 6.0
uint Preroll(void) const { return _data[6]; }
// dtmf_count 3 7.0
uint DTMFCount(void) const { return _data[7]>>5; }
// reserved 5 7.3
// for (i = 0; i < dtmf_count; i++)
// DTMF_char 8 8.0+i
char DTMFChar(uint i) const { return _data[8+i]; }
QString DTMFString(void) const
{
QByteArray ba(reinterpret_cast<const char*>(_data+8), DTMFCount());
return QString(ba);
}

static bool IsParsible(const unsigned char *data, uint safe_bytes);

virtual QString toString(void) const
{
return QString("Splice DTMF: %1").arg(DTMFString());
}
};

class SegmentationDescriptor : SpliceDescriptor
{
public:
SegmentationDescriptor(const unsigned char *data, int len = 300) :
SpliceDescriptor(data, len, SpliceDescriptorID::segmentation) { }

// Name bits loc expected value
// splice_descriptor_tag 8 0.0 0x01
// descriptor_length 8 1.0
// identifier 32 2.0 0x43554549 "CUEI"
// segmentation_event_id 32 6.0
uint SegmentationEventId(void) const
{
return (_data[2]<<24) | (_data[3]<<16) | (_data[4]<<8) | _data[5];
}
QString SegmentationEventIdString(void) const
{
return QString(QChar(_data[6])) + QChar(_data[7]) +
QChar(_data[8]) + QChar(_data[9]);
}
// segmentation_event_cancel_indicator 1 10.0
bool IsSegmentationEventCancel(void) const { return _data[10] & 0x80; }
// reserved 7 10.1
// if (segmentation_event_cancel_indicator == ‘0’) {
// program_seg_flag 1 11.0
bool IsProgramSegmentation(void) const { return _data[11] & 0x80; }
// seg_duration_flag 1 11.1
bool HasSegmentationDuration(void) const { return _data[11] & 0x40; }
// reserved 6 11.2
// if (program_segmentation_flag == ‘0’) {
// component_count 8 12
uint ComponentCount(void) const { return _data[12]; }
// for (i = 0; i < component_count; i++) {
// component_tag 8 13 + i * 6
uint ComponentTag(uint i) const { return _data[13 + i * 6]; }
// reserved 7 14.1 + i * 6
// pts_offset 33 14.7 + i * 6
uint64_t PTSOffset(uint i)
{
return ((uint64_t(_data[14+(i*6)] & 0x1) << 32) |
(uint64_t(_data[15+(i*6)]) << 24) |
(uint64_t(_data[16+(i*6)]) << 16) |
(uint64_t(_data[17+(i*6)]) << 8) |
(uint64_t(_data[18+(i*6)])));
}
// }
// }
// if(segmentation_duration_flag == ‘1’)
// segmentation_duration 40 _ptrs[0]
uint64_t SegmentationDuration(void) const
{
return ((uint64_t(_ptrs[0][0]) << 32) |
(uint64_t(_ptrs[0][1]) << 24) |
(uint64_t(_ptrs[0][2]) << 16) |
(uint64_t(_ptrs[0][3]) << 8) |
(uint64_t(_ptrs[0][4])));
}
// segmentation_upid_type 8 _ptrs[1]
enum
{
kNotUsed = 0x0, ///< upid is not present in the descriptor
kVariable = 0x1, ///< user defined
kISCI = 0x2, ///< 4 alpha + 4 numeric
kAdID = 0x3, ///< 4 alpha + 4 alphanumeric
kUMID = 0x4, ///< UMID See SMPTE 330M
kISAN = 0x5, ///< ISAN See ISO 15706
kVISAN = 0x6, ///< versioned ISAN See ISO 15706-2
kTID = 0x7, ///< TMS ProgramID 2 alpha followed by 10 numeric
kTI = 0x8, ///< Turner Identifier
kADI = 0x9, ///< ADI See MD-SP-ADI2.0-AS-I03-070105
};
uint SegmentationUPIDType(void) const { return _ptrs[1][0]; }
// segmentation_upid_length 8 _ptrs[1]+1
uint SegmentationUPIDLength(void) const
{ return _ptrs[1][1]; }
// segmentation_upid() ? _ptrs[1]+2
const unsigned char *SegmentationUPID(void) const
{
return _ptrs[1]+2;
}
QString SegmentationUPIDString(void) const
{
QByteArray ba(reinterpret_cast<const char*>(_ptrs[1]+2),
SegmentationUPIDLength());
return QString(ba);
}

enum
{
kNotIndicated = 0x00,
kContentIdentification = 0x01,
kProgramStart = 0x10,
kProgramEnd = 0x11,
kProgramEarlyTermination = 0x12,
kProgramBreakaway = 0x13,
kProgramResumption = 0x14,
kProgramRunoverPlanned = 0x15,
kProgramRunoverUnplanned = 0x16,
kChapterStart = 0x20,
kChapterEnd = 0x21,
kProviderAdvertisementStart = 0x30,
kProviderAdvertisementEnd = 0x31,
kDistributorAdvertisementStart = 0x32,
kDistributorAdvertisementEnd = 0x33,
kUnscheduledEventStart = 0x40,
kUnscheduledEventEnd = 0x41,
};
// segmentation_type_id 8 _ptrs[2]
uint SegmentationTypeID(void) const { return _ptrs[2][0]; }
// segment_num 8 _ptrs[2]+1
uint SegmentNum(void) const { return _ptrs[2][1]; }
// segments_expected 8 _ptrs[2]+2
uint SegmentsExpected(void) const { return _ptrs[2][2]; }
// }

virtual bool Parse(void);
QString toString(void) const;

// _ptrs[0] = program_segmentation_flag ? 12 : 13 + component_count * 6
// _ptrs[1] = _ptrs[0] + HasSegmentationDuration() ? 5 : 0
// _ptrs[2] = _ptrs[1] + 2 + SegmentationUPIDLength()
unsigned char const * _ptrs[3];
};