mistserver/lib/ts_packet.cpp
2021-10-19 22:29:40 +02:00

1452 lines
56 KiB
C++

/// \file ts_packet.cpp
/// Holds all code for the TS namespace.
#include "bitfields.h"
#include "defines.h"
#include "ts_packet.h"
#include "util.h"
#include <iomanip>
#include <map>
#include <set>
#include <sstream>
#include <string.h>
#ifndef FILLER_DATA
#define FILLER_DATA \
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent commodo vulputate urna eu " \
"commodo. Cras tempor velit nec nulla placerat volutpat. Proin eleifend blandit quam sit amet " \
"suscipit. Pellentesque vitae tristique lorem. Maecenas facilisis consequat neque, vitae " \
"iaculis eros vulputate ut. Suspendisse ut arcu non eros vestibulum pulvinar id sed erat. Nam " \
"dictum tellus vel tellus rhoncus ut mollis tellus fermentum. Fusce volutpat consectetur ante, " \
"in mollis nisi euismod vulputate. Curabitur vitae facilisis ligula. Sed sed gravida dolor. " \
"Integer eu eros a dolor lobortis ullamcorper. Mauris interdum elit non neque interdum dictum. " \
"Suspendisse imperdiet eros sed sapien cursus pulvinar. Vestibulum ut dolor lectus, id commodo " \
"elit. Cras convallis varius leo eu porta. Duis luctus sapien nec dui adipiscing quis interdum " \
"nunc congue. Morbi pharetra aliquet mauris vitae tristique. Etiam feugiat sapien quis augue " \
"elementum id ultricies magna vulputate. Phasellus luctus, leo id egestas consequat, eros " \
"tortor commodo neque, vitae hendrerit nunc sem ut odio."
#endif
std::set<unsigned int> pmt_pids;
std::map<unsigned int, std::string> stream_pids;
namespace TS{
/// This constructor creates an empty Packet, ready for use for either reading or writing.
/// All this constructor does is call Packet::clear().
Packet::Packet(){
pos = 0;
clear();
}
Packet::Packet(const Packet &rhs){
memcpy(strBuf, rhs.strBuf, 188);
pos = 188;
}
/// This function fills a Packet from a file.
/// It fills the content with the next 188 bytes int he file.
/// \param Data The data to be read into the packet.
/// \return true if it was possible to read in a full packet, false otherwise.
bool Packet::FromFile(FILE *data){
uint64_t bPos = Util::ftell(data);
uint16_t retries = 0;
while (retries < 256){
if (!fread((void *)strBuf, 188, 1, data)){
if (!feof(data)){FAIL_MSG("Could not read 188 bytes from file! %s", strerror(errno));}
return false;
}
if (strBuf[0] == 0x47){
pos = 188;
return true;
}
for (uint8_t i = 1; i < 188; ++i){
if (strBuf[i] == 0x47){
INFO_MSG("Shifting %u bytes", i);
memmove((void *)strBuf, (void *)(strBuf + i), 188 - i);
if (!fread((void *)strBuf, i, 1, data)){return false;}
pos = 188;
return true;
}
}
INFO_MSG("Skipping invalid TS packet...");
}
FAIL_MSG("Failed to read a good packet @ %" PRIu64 " bytes", bPos);
return false;
}
bool Packet::FromStream(std::istream &data){
long long int bPos = data.tellg();
if (!data.read(strBuf, 188)){
if (!data.eof()){HIGH_MSG("failed to read 188 bytes: %s", strerror(errno));}
return false;
}
if (strBuf[0] != 0x47){
HIGH_MSG("Failed to read a good packet on pos %lld", bPos);
return false;
}
pos = 188;
return true;
}
/// This funtion fills a Packet from
/// a char array. It fills the content with
/// the first 188 characters of a char array
///\param Data The char array that contains the data to be read into the packet
///\return true if successful (which always happens, or else a segmentation fault should occur)
bool Packet::FromPointer(const char *data){
memcpy((void *)strBuf, (void *)data, 188);
pos = 188;
return true;
}
/// The deconstructor deletes all space that may be occupied by a Packet.
Packet::~Packet(){}
/// update position in character array (pos),
void Packet::updPos(unsigned int newPos){
if (pos < newPos){pos = newPos;}
}
/// Sets the PID of a single Packet.
/// \param NewPID The new PID of the packet.
void Packet::setPID(int NewPID){
strBuf[1] = (strBuf[1] & 0xE0) + ((NewPID & 0x1F00) >> 8);
strBuf[2] = (NewPID & 0x00FF);
updPos(2);
}
/// Gets the PID of a single Packet.
/// \return The value of the PID.
unsigned int Packet::getPID() const{
return (unsigned int)(((strBuf[1] & 0x1F) << 8) + strBuf[2]);
}
/// Sets the Continuity Counter of a single Packet.
/// \param NewContinuity The new Continuity Counter of the packet.
void Packet::setContinuityCounter(int NewContinuity){
strBuf[3] = (strBuf[3] & 0xF0) | (NewContinuity & 0x0F);
updPos(3);
}
/// Gets the Continuity Counter of a single Packet.
/// \return The value of the Continuity Counter.
int Packet::getContinuityCounter() const{return (strBuf[3] & 0x0F);}
/// Gets the amount of bytes that are not written yet in a Packet.
/// \return The amount of bytes that can still be written to this packet.
unsigned int Packet::getBytesFree() const{
if (pos > 188){
FAIL_MSG("pos is > 188. Actual pos: %d segfaulting gracefully :)", pos);
((char *)0)[0] = 1;
}
return 188 - pos;
}
/// Sets the packet pos to 4, and resets the first 4 fields to defaults (including sync byte on pos 0)
void Packet::clear(){
memset(strBuf, (char)0, 188);
strBuf[0] = 0x47;
strBuf[1] = 0x00;
strBuf[2] = 0x00;
strBuf[3] = 0x10;
pos = 4;
}
/// Sets the selection value for an adaptationfield of a Packet.
/// \param NewSelector The new value of the selection bits.
/// - 1: No AdaptationField.
/// - 2: AdaptationField Only.
/// - 3: AdaptationField followed by Data.
void Packet::setAdaptationField(int NewSelector){
strBuf[3] = (strBuf[3] & 0xCF) + ((NewSelector & 0x03) << 4);
if (NewSelector & 0x02){
strBuf[4] = 0x00;
}else{
pos = 4;
}
}
/// Gets whether a Packet contains an adaptationfield.
/// \return The existence of an adaptationfield.
/// - 0: No adaptationfield present.
/// - 1: Adaptationfield is present.
int Packet::getAdaptationField() const{return ((strBuf[3] & 0x30) >> 4);}
/// Sets the PCR (Program Clock Reference) of a Packet.
/// \param NewVal The new PCR Value.
void Packet::setPCR(int64_t NewVal){
updPos(12);
setAdaptationField(3);
strBuf[4] = 0x07;
strBuf[5] = (strBuf[5] | 0x10);
int64_t TmpVal = NewVal / 300;
strBuf[6] = (((TmpVal >> 1) >> 24) & 0xFF);
strBuf[7] = (((TmpVal >> 1) >> 16) & 0xFF);
strBuf[8] = (((TmpVal >> 1) >> 8) & 0xFF);
strBuf[9] = ((TmpVal >> 1) & 0xFF);
int Remainder = NewVal % 300;
strBuf[10] = 0x7E + ((TmpVal & 0x01) << 7) + ((Remainder & 0x0100) >> 8);
strBuf[11] = (Remainder & 0x00FF);
}
/// Gets the PCR (Program Clock Reference) of a Packet.
/// \return The value of the PCR.
int64_t Packet::getPCR() const{
if (!getAdaptationField()){return -1;}
if (!(strBuf[5] & 0x10)){return -1;}
int64_t Result = (((strBuf[6] << 24) | (strBuf[7] << 16) | (strBuf[8] << 8) | strBuf[9]) << 1) |
(strBuf[10] >> 7);
Result *= 300;
Result |= (((strBuf[10] & 0x01) << 8) + strBuf[11]);
return Result;
}
/// Gets the OPCR (Original Program Clock Reference) of a Packet.
/// \return The value of the OPCR.
int64_t Packet::getOPCR() const{
if (!getAdaptationField()){return -1;}
if (!(strBuf[5 + 6] & 0x10)){return -1;}
int64_t Result = 0;
Result = (((strBuf[6 + 6] << 24) + (strBuf[7 + 6] << 16) + (strBuf[8 + 6] << 8) + strBuf[9 + 6]) << 1) +
(strBuf[10 + 6] & 0x80 >> 7);
Result = Result * 300;
Result += (((strBuf[10 + 6] & 0x01) << 8) + strBuf[11 + 6]);
return Result;
}
/// Gets the transport error inficator of a Packet
/// \return The transport error inficator of a Packet
bool Packet::hasTransportError() const{return strBuf[1] & 0x80;}
/// Gets the payload unit start inficator of a Packet
/// \return The payload unit start inficator of a Packet
bool Packet::getUnitStart() const{return strBuf[1] & 0x40;}
/// Gets the transport priority of a Packet
/// \return The transport priority of a Packet
bool Packet::hasPriority() const{return strBuf[1] & 0x20;}
/// Gets the transport scrambling control of a Packet
/// \return The transport scrambling control of a Packet
unsigned int Packet::getTransportScramblingControl() const{
return (unsigned int)((strBuf[3] >> 6) & (0x03));
}
/// Gets the current length of the adaptationfield.
/// \return The length of the adaptationfield.
int Packet::getAdaptationFieldLen() const{
if (!getAdaptationField()){return -1;}
return (int)strBuf[4];
}
Packet::operator bool() const{return pos && strBuf[0] == 0x47;}
/// Prints a packet to stdout, for analyser purposes.
/// If detail level contains bitmask 64, prints raw bytes after packet.
std::string Packet::toPrettyString(size_t indent, int detailLevel) const{
if (!(*this)){return "[Invalid packet - no sync byte]";}
std::stringstream output;
output << std::string(indent, ' ') << "[PID " << getPID() << "|" << std::hex
<< getContinuityCounter() << std::dec << ": " << getDataSize() << "b ";
switch (getPID()){
case 0: output << "PAT"; break;
case 1: output << "CAT"; break;
case 2: output << "TSDT"; break;
case 17: output << "SDT"; break;
case 0x1FFF: output << "Null"; break;
default:
if (isPMT()){
output << "PMT";
}else{
if (isStream()){
output << stream_pids[getPID()];
}else{
output << "Unknown";
}
}
break;
}
output << "]";
if (getUnitStart()){output << " [Start]";}
if (getAdaptationField() > 1 && getAdaptationFieldLen()){
if (hasDiscontinuity()){output << " [Discontinuity]";}
if (getRandomAccess()){output << " [RandomXS]";}
if (getESPriority()){output << " [ESPriority]";}
if (hasPCR()){output << " [PCR " << (double)getPCR() / 27000000 << "s]";}
if (hasOPCR()){output << " [OPCR: " << (double)getOPCR() / 27000000 << "s]";}
}
output << std::endl;
if (!getPID()){
// PAT
output << ((ProgramAssociationTable *)this)->toPrettyString(indent + 2);
return output.str();
}
if (pmt_pids.count(getPID())){
// PMT
output << ((ProgramMappingTable *)this)->toPrettyString(indent + 2);
return output.str();
}
if (getPID() == 17){
// SDT
output << ((ServiceDescriptionTable *)this)->toPrettyString(indent + 2);
return output.str();
}
if (detailLevel & 64){
output << std::string(indent + 2, ' ') << "Raw data bytes:";
unsigned int size = getDataSize();
for (unsigned int i = 0; i < size; ++i){
if (!(i % 32)){output << std::endl << std::string(indent + 4, ' ');}
output << std::hex << std::setw(2) << std::setfill('0')
<< (unsigned int)(strBuf + 188 - size)[i] << " ";
if ((i % 4) == 3){output << " ";}
}
output << std::endl;
}
return output.str();
}
unsigned int Packet::getDataSize() const{
return 184 - ((getAdaptationField() > 1) ? getAdaptationFieldLen() + 1 : 0);
}
/// Returns true if this PID contains a PMT.
/// Important caveat: only works if the corresponding PAT has been pretty-printed or had parsePIDs() called on it!
bool Packet::isPMT() const{return pmt_pids.count(getPID());}
/// Returns true if this PID contains a stream known from a PMT.
/// Important caveat: only works if the corresponding PMT was pretty-printed or had parseStreams() called on it!
bool Packet::isStream() const{return stream_pids.count(getPID());}
/// Sets the start of a new unit in this Packet.
/// \param NewVal The new value for the start of a unit.
void Packet::setUnitStart(bool NewVal){
if (NewVal){
strBuf[1] |= 0x40;
}else{
strBuf[1] &= 0xBF;
}
}
bool Packet::hasDiscontinuity() const{return strBuf[5] & 0x80;}
void Packet::setDiscontinuity(bool newVal){
updPos(6);
if (getAdaptationField() == 3){
if (!strBuf[4]){strBuf[4] = 1;}
if (newVal){
strBuf[5] |= 0x80;
}else{
strBuf[5] &= 0x7F;
}
}else{
setAdaptationField(3);
strBuf[4] = 1;
if (newVal){
strBuf[5] = 0x80;
}else{
strBuf[5] = 0x00;
}
}
}
/// Gets whether this Packet can be accessed at random (indicates keyframe).
/// \return Whether or not this Packet contains a keyframe.
bool Packet::getRandomAccess() const{
if (getAdaptationField() < 2){return false;}
return strBuf[5] & 0x40;
}
/// Gets whether this Packet has the priority bit set
/// \return Whether or not this Packet has the priority bit set
bool Packet::getESPriority() const{
if (getAdaptationField() < 2){return false;}
return strBuf[5] & 0x20;
}
/// Gets the value of the PCR flag
///\return true if there is a PCR, false otherwise
bool Packet::hasPCR() const{return strBuf[5] & 0x10;}
/// Gets the value of the OPCR flag
///\return true if there is an OPCR, false otherwise
bool Packet::hasOPCR() const{return strBuf[5] & 0x08;}
/// Gets the value of the splicing point flag
///\return the value of the splicing point flag
bool Packet::hasSplicingPoint() const{return strBuf[5] & 0x04;}
/// Gets the value of the transport private data point flag
///\return the value of the transport private data point flag
void Packet::setRandomAccess(bool NewVal){
updPos(6);
if (getAdaptationField() == 3){
if (!strBuf[4]){strBuf[4] = 1;}
if (NewVal){
strBuf[5] |= 0x40;
}else{
strBuf[5] &= 0xBF;
}
}else{
setAdaptationField(3);
strBuf[4] = 1;
if (NewVal){
strBuf[5] = 0x40;
}else{
strBuf[5] = 0x00;
}
}
}
/// Gets the value of the ES priority flag
///\return the value of the ES priority flag
void Packet::setESPriority(bool NewVal){
updPos(6);
if (getAdaptationField() == 3){
if (!strBuf[4]){strBuf[4] = 1;}
if (NewVal){
strBuf[5] |= 0x20;
}else{
strBuf[5] &= 0xDF;
}
}else{
setAdaptationField(3);
strBuf[4] = 1;
if (NewVal){
strBuf[5] = 0x20;
}else{
strBuf[5] = 0x00;
}
}
}
/// Transforms the Packet into a standard Program Association Table
void Packet::setDefaultPAT(){
static int MyCntr = 0;
memcpy((void *)strBuf, (void *)PAT, 188);
pos = 188;
setContinuityCounter(MyCntr++);
MyCntr %= 0x10;
}
/// Checks the size of the internal packet buffer (prints error if size !=188), then returns a
/// pointer to the data. \return A character pointer to the internal packet buffer data
const char *Packet::checkAndGetBuffer() const{
if (pos != 188){HIGH_MSG("Size invalid (%d) - invalid data from this point on", pos);}
return strBuf;
}
// BEGIN PES FUNCTIONS
// pes functons do not use the internal strBuf character buffer
///\brief Appends the PES-encoded timestamp to a string.
///\param strBuf The string to append to
///\param fixedLead The "fixed" 4-bit lead value to use
///\param time The timestamp to encode
void encodePESTimestamp(std::string &tmpBuf, char fixedLead, unsigned long long time){
// FixedLead of 4 bits, bits 32-30 time, 1 marker bit
tmpBuf += (char)(fixedLead | ((time & 0x1C0000000LL) >> 29) | 0x01);
// Bits 29-22 time
tmpBuf += (char)((time & 0x03FC00000LL) >> 22);
// Bits 21-15 time, 1 marker bit
tmpBuf += (char)(((time & 0x0003F8000LL) >> 14) | 0x01);
// Bits 14-7 time
tmpBuf += (char)((time & 0x000007F80LL) >> 7);
// Bits 7-0 time, 1 marker bit
tmpBuf += (char)(((time & 0x00000007FLL) << 1) | 0x01);
}
/// Generates a PES Lead-in for a video frame.
/// Prepends the lead-in to variable toSend, assumes toSend's length is all other data.
/// \param len The length of this frame.
/// \param PTS The timestamp of the frame.
std::string &Packet::getPESVideoLeadIn(unsigned int len, unsigned long long PTS,
unsigned long long offset, bool isAligned, uint64_t bps){
if (len){len += (offset ? 13 : 8);}
if (bps >= 50){
if (len){len += 3;}
}else{
bps = 0;
}
static std::string tmpStr;
tmpStr.clear();
tmpStr.reserve(25);
tmpStr.append("\000\000\001\340", 4);
tmpStr += (char)((len >> 8) & 0xFF);
tmpStr += (char)(len & 0xFF);
if (isAligned){
tmpStr.append("\204", 1);
}else{
tmpStr.append("\200", 1);
}
tmpStr += (char)((offset ? 0xC0 : 0x80) | (bps ? 0x10 : 0)); // PTS/DTS + Flags
tmpStr += (char)((offset ? 10 : 5) + (bps ? 3 : 0)); // PESHeaderDataLength
encodePESTimestamp(tmpStr, (offset ? 0x30 : 0x20), PTS + offset);
if (offset){encodePESTimestamp(tmpStr, 0x10, PTS);}
if (bps){
char rate_buf[3];
Bit::htob24(rate_buf, (bps / 50) | 0x800001);
tmpStr.append(rate_buf, 3);
}
return tmpStr;
}
/// Generates a PES Lead-in for an audio frame.
/// Prepends the lead-in to variable toSend, assumes toSend's length is all other data.
/// \param len The length of this frame.
/// \param PTS The timestamp of the frame.
std::string &Packet::getPESAudioLeadIn(unsigned int len, unsigned long long PTS, uint64_t bps){
if (bps >= 50){
len += 3;
}else{
bps = 0;
}
static std::string tmpStr;
tmpStr.clear();
tmpStr.reserve(20);
len += 8;
tmpStr.append("\000\000\001\300", 4);
tmpStr += (char)((len & 0xFF00) >> 8); // PES PacketLength
tmpStr += (char)(len & 0x00FF); // PES PacketLength (Cont)
tmpStr += (char)0x84; // isAligned
tmpStr += (char)(0x80 | (bps ? 0x10 : 0)); // PTS/DTS + Flags
tmpStr += (char)(5 + (bps ? 3 : 0)); // PESHeaderDataLength
encodePESTimestamp(tmpStr, 0x20, PTS);
if (bps){
char rate_buf[3];
Bit::htob24(rate_buf, (bps / 50) | 0x800001);
tmpStr.append(rate_buf, 3);
}
return tmpStr;
}
// END PES FUNCTIONS
/// Fills the free bytes of the Packet.
/// Stores as many bytes from NewVal as possible in the packet.
/// The minimum of Packet::BytesFree and maxLen is used.
/// \param NewVal The data to store in the packet.
/// \param maxLen The maximum amount of bytes to store.
int Packet::fillFree(const char *NewVal, int maxLen){
int toWrite = std::min((int)getBytesFree(), maxLen);
memcpy((void *)(strBuf + pos), (void *)NewVal, toWrite);
pos += toWrite;
return toWrite;
}
/// Adds stuffing to the Packet depending on how much content you want to send.
/// \param NumBytes the amount of non-stuffing content bytes you want to send.
/// \return The amount of content bytes that can be send.
void Packet::addStuffing(){
unsigned int numBytes = getBytesFree();
if (!numBytes){return;}
if (getAdaptationField() == 2){
FAIL_MSG("Can not handle adaptation field 2 - should stuff the entire packet, no data will "
"follow after the adaptation field"); ///\todo more stuffing required
return;
}
if (getAdaptationField() == 1){
// Convert adaptationfield to 3, by shifting the \001 at [4] (and all after it) to [5].
if (numBytes == 184){
strBuf[pos++] = 0x0; // strBuf.append("\000", 1);
}else{
// strBuf.insert(4, 1, (char)0);
memmove((void *)(strBuf + 5), (void *)(strBuf + 4), 188 - 4 - 1);
pos++;
}
setAdaptationField(3); // sets [4] to 0
}
numBytes = getBytesFree(); // get the most recent strBuf len before stuffing
if (getAdaptationField() == 3 && numBytes){
if (strBuf[4] == 0){// if we already have stuffing
memmove((void *)(strBuf + 5 + numBytes), (void *)(strBuf + 5), 188 - 5 - numBytes);
memset((void *)(strBuf + 5), '$', numBytes);
pos += numBytes;
}else{
memmove((void *)(strBuf + 5 + strBuf[4] + numBytes), (void *)(strBuf + 5 + strBuf[4]),
188 - 5 - strBuf[4] - numBytes);
memset((void *)(strBuf + 5 + strBuf[4]), '$', numBytes);
pos += numBytes;
}
strBuf[4] += numBytes; // add stuffing to the stuffing counter at [4]
}
if (numBytes){// if we added stuffing...
if (numBytes == strBuf[4]){// and the stuffing is ALL the stuffing...
strBuf[5] = 0x00; // set [5] to zero for some reason...?
numBytes--; // decrease the stuffing needed by one
}
// overwrite bytes [5+currStuffing-newStuffing] onward with newStuffing bytes of prettier filler data
for (int i = 0; i < numBytes; i++){
strBuf[5 + (strBuf[4] - numBytes) + i] = FILLER_DATA[i % sizeof(FILLER_DATA)];
}
}
}
/// returns the character buffer with a std::string wrapper
///\return The raw TS data as a string
// const std::string& Packet::getStrBuf() const{
// return std::string(strBuf);
//}
/// Gets the payload of this packet, as a raw char array
///\return The payload of this ts packet as a char pointer
const char *Packet::getPayload() const{
return strBuf + (4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0));
}
/// Gets the length of the payload for this apcket
///\return The amount of bytes payload in this packet
int Packet::getPayloadLength() const{
return 184 - ((getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0));
}
ProgramAssociationTable &ProgramAssociationTable::operator=(const Packet &rhs){
memcpy(strBuf, rhs.checkAndGetBuffer(), 188);
pos = 188;
return *this;
}
/// Retrieves the current addStuffingoffset value for a PAT
char ProgramAssociationTable::getOffset() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0);
return strBuf[loc];
}
/// Retrieves the ID of this table
char ProgramAssociationTable::getTableId() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 1;
return strBuf[loc];
}
/// Retrieves the current section length
short ProgramAssociationTable::getSectionLength() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 2;
return (short)(strBuf[loc] & 0x0F) << 8 | strBuf[loc + 1];
}
/// Retrieves the Transport Stream ID
short ProgramAssociationTable::getTransportStreamId() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 4;
return (short)(strBuf[loc] & 0x0F) << 8 | strBuf[loc + 1];
}
/// Retrieves the version number
char ProgramAssociationTable::getVersionNumber() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 6;
return (strBuf[loc] >> 1) & 0x1F;
}
/// Retrieves the "current/next" indicator
bool ProgramAssociationTable::getCurrentNextIndicator() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 6;
return strBuf[loc] & 0x01;
}
/// Retrieves the section number
char ProgramAssociationTable::getSectionNumber() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 7;
return strBuf[loc];
}
/// Retrieves the last section number
char ProgramAssociationTable::getLastSectionNumber() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 8;
return strBuf[loc];
}
/// Returns the amount of programs in this table
short ProgramAssociationTable::getProgramCount() const{
// This is correct, not -12 since we already parsed 4 bytes here
return (getSectionLength() - 8) / 4;
}
short ProgramAssociationTable::getProgramNumber(short index) const{
if (index > getProgramCount()){return 0;}
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 9;
return ((short)(strBuf[loc + (index * 4)]) << 8) | strBuf[loc + (index * 4) + 1];
}
short ProgramAssociationTable::getProgramPID(short index) const{
if (index > getProgramCount()){return 0;}
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 9;
return (((short)(strBuf[loc + (index * 4) + 2]) << 8) | strBuf[loc + (index * 4) + 3]) & 0x1FFF;
}
int ProgramAssociationTable::getCRC() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) +
getOffset() + 9 + (getProgramCount() * 4);
return ((int)(strBuf[loc]) << 24) | ((int)(strBuf[loc + 1]) << 16) |
((int)(strBuf[loc + 2]) << 8) | strBuf[loc + 3];
}
void ProgramAssociationTable::parsePIDs(){
for (int i = 0; i < getProgramCount(); i++){pmt_pids.insert(getProgramPID(i));}
}
/// This function prints a program association table,
/// prints all values in a human readable format
///\param indent The indentation of the string printed as wanted by the user
///\return The string with human readable data from a PAT
std::string ProgramAssociationTable::toPrettyString(size_t indent) const{
std::stringstream output;
output << std::string(indent, ' ') << "[Program Association Table]" << std::endl;
output << std::string(indent + 2, ' ') << "Pointer Field: " << (int)getOffset() << std::endl;
output << std::string(indent + 2, ' ') << "Table ID: " << (int)getTableId() << std::endl;
output << std::string(indent + 2, ' ') << "Sectionlen: " << getSectionLength() << std::endl;
output << std::string(indent + 2, ' ') << "Transport Stream ID: " << getTransportStreamId() << std::endl;
output << std::string(indent + 2, ' ') << "Version Number: " << (int)getVersionNumber() << std::endl;
output << std::string(indent + 2, ' ')
<< "Current/Next Indicator: " << (int)getCurrentNextIndicator() << std::endl;
output << std::string(indent + 2, ' ') << "Section number: " << (int)getSectionNumber() << std::endl;
output << std::string(indent + 2, ' ') << "Last Section number: " << (int)getLastSectionNumber()
<< std::endl;
output << std::string(indent + 2, ' ') << "Programs [" << (int)getProgramCount() << "]" << std::endl;
for (int i = 0; i < getProgramCount(); i++){
output << std::string(indent + 4, ' ') << "[" << i + 1 << "] ";
output << "Program Number: " << getProgramNumber(i) << ", ";
output << (getProgramNumber(i) == 0 ? "Network" : "Program Map") << " PID: " << getProgramPID(i);
output << std::endl;
}
output << std::string(indent + 2, ' ') << "CRC32: " << std::hex << std::setw(8)
<< std::setfill('0') << std::uppercase << getCRC() << std::dec << std::endl;
return output.str();
}
ProgramMappingEntry::ProgramMappingEntry(char *begin, char *end){
data = begin;
boundary = end;
}
ProgramMappingEntry::operator bool() const{return data && (data < boundary);}
int ProgramMappingEntry::getStreamType() const{return data[0];}
void ProgramMappingEntry::setStreamType(int newType){data[0] = newType;}
std::string ProgramMappingEntry::getCodec() const{
switch (getStreamType()){
case 0x01:
case 0x03: return "MPEG1";
case 0x02: return "MPEG1/2";
case 0x04:
case 0x05:
case 0x06: return "MPEG2";
case 0x07: return "MHEG";
case 0x08: return "MPEG2 DSM CC";
case 0x09: return "H.222.1";
case 0x0A: return "DSM CC encapsulation";
case 0x0B: return "DSM CC U-N";
case 0x0C: return "DSM CC descriptor";
case 0x0D: return "DSM CC section";
case 0x0E: return "MPEG2 aux";
case 0x0F: return "ADTS";
case 0x10: return "MPEG4";
case 0x11: return "LATM";
case 0x12: return "SL/Flex PES";
case 0x13: return "SL/Flex section";
case 0x14: return "SDP";
case 0x15: return "meta PES";
case 0x16: return "meta section";
case 0x1B: return "H264";
case 0x24: return "HEVC";
case 0x81: return "AC3";
default: return "unknown";
}
}
std::string ProgramMappingEntry::getStreamTypeString() const{
switch (getStreamType()){
case 0x01:
case 0x02:
case 0x09:
case 0x10:
case 0x24:
case 0x1B: return "video";
case 0x03:
case 0x04:
case 0x11:
case 0x81:
case 0x0F: return "audio";
default: return "data";
}
}
int ProgramMappingEntry::getElementaryPid() const{return ((data[1] << 8) | data[2]) & 0x1FFF;}
void ProgramMappingEntry::setElementaryPid(int newElementaryPid){
data[1] = (newElementaryPid >> 8 & 0x1F) | 0xE0; // 0xE0 = three reserved bits
data[2] = newElementaryPid & 0xFF;
}
int ProgramMappingEntry::getESInfoLength() const{return ((data[3] << 8) | data[4]) & 0x0FFF;}
const char *ProgramMappingEntry::getESInfo() const{return data + 5;}
void ProgramMappingEntry::setESInfo(const std::string &newInfo){
data[3] = ((newInfo.size() >> 8) & 0x0F) | 0xF0; // 0xF0 = four reserved bits
data[4] = newInfo.size() & 0xFF;
memcpy(data + 5, newInfo.data(), newInfo.size());
}
void ProgramMappingEntry::advance(){
if (!(*this)){return;}
data += 5 + getESInfoLength();
}
ProgramMappingTable::ProgramMappingTable(){
strBuf[0] = 0x47;
strBuf[1] = 0x50;
strBuf[2] = 0x00;
strBuf[3] = 0x10;
pos = 4;
}
ProgramMappingTable &ProgramMappingTable::operator=(const Packet &rhs){
memcpy(strBuf, rhs.checkAndGetBuffer(), 188);
pos = 188;
return *this;
}
char ProgramMappingTable::getOffset() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0);
return strBuf[loc];
}
void ProgramMappingTable::setOffset(char newVal){
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0);
strBuf[loc] = newVal;
}
char ProgramMappingTable::getTableId() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 1;
return strBuf[loc];
}
void ProgramMappingTable::setTableId(char newVal){
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 1;
updPos(loc + 1);
strBuf[loc] = newVal;
}
short ProgramMappingTable::getSectionLength() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 2;
return (((short)strBuf[loc] & 0x0F) << 8) | strBuf[loc + 1];
}
void ProgramMappingTable::setSectionLength(short newVal){
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 2;
updPos(loc + 2);
strBuf[loc] = (char)(newVal >> 8);
strBuf[loc + 1] = (char)newVal;
}
short ProgramMappingTable::getProgramNumber() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 4;
return (((short)strBuf[loc]) << 8) | strBuf[loc + 1];
}
void ProgramMappingTable::setProgramNumber(short newVal){
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 4;
updPos(loc + 2);
strBuf[loc] = (char)(newVal >> 8);
strBuf[loc + 1] = (char)newVal;
}
char ProgramMappingTable::getVersionNumber() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 6;
return (strBuf[loc] >> 1) & 0x1F;
}
void ProgramMappingTable::setVersionNumber(char newVal){
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 6;
updPos(loc + 1);
strBuf[loc] = ((newVal & 0x1F) << 1) | (strBuf[loc] & 0xC1);
}
/// Retrieves the "current/next" indicator
bool ProgramMappingTable::getCurrentNextIndicator() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 6;
return strBuf[loc] & 0x01;
}
/// Sets the "current/next" indicator
void ProgramMappingTable::setCurrentNextIndicator(bool newVal){
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 6;
updPos(loc + 1);
strBuf[loc] = (newVal ? 1 : 0) | (strBuf[loc] & 0xFE);
}
/// Retrieves the section number
char ProgramMappingTable::getSectionNumber() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 7;
return strBuf[loc];
}
/// Sets the section number
void ProgramMappingTable::setSectionNumber(char newVal){
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 7;
updPos(loc + 1);
strBuf[loc] = newVal;
}
/// Retrieves the last section number
char ProgramMappingTable::getLastSectionNumber() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 8;
return strBuf[loc];
}
/// Sets the last section number
void ProgramMappingTable::setLastSectionNumber(char newVal){
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 8;
updPos(loc + 1);
strBuf[loc] = newVal;
}
short ProgramMappingTable::getPCRPID() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 9;
return (((short)strBuf[loc] & 0x1F) << 8) | strBuf[loc + 1];
}
void ProgramMappingTable::setPCRPID(short newVal){
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 9;
updPos(loc + 2);
strBuf[loc] = (char)((newVal >> 8) & 0x1F) | 0xE0; // Note: here we set reserved bits on 1
strBuf[loc + 1] = (char)newVal;
}
short ProgramMappingTable::getProgramInfoLength() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 11;
return (((short)strBuf[loc] & 0x0F) << 8) | strBuf[loc + 1];
}
void ProgramMappingTable::setProgramInfoLength(short newVal){
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 11;
updPos(loc + 2);
strBuf[loc] = (char)((newVal >> 8) & 0x0F) | 0xF0; // Note: here we set reserved bits on 1
strBuf[loc + 1] = (char)newVal;
}
ProgramMappingEntry ProgramMappingTable::getEntry(int index) const{
int dataOffset = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset();
ProgramMappingEntry res((char *)(strBuf + dataOffset + 13 + getProgramInfoLength()),
(char *)(strBuf + dataOffset + getSectionLength()));
for (int i = 0; i < index; i++){res.advance();}
return res;
}
int ProgramMappingTable::getCRC() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) +
getOffset() + getSectionLength();
return ((int)(strBuf[loc]) << 24) | ((int)(strBuf[loc + 1]) << 16) |
((int)(strBuf[loc + 2]) << 8) | strBuf[loc + 3];
}
void ProgramMappingTable::calcCRC(){
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) +
getOffset() + getSectionLength();
unsigned int newVal; // this will hold the CRC32 value;
unsigned int pidLoc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) +
getOffset() + 1; // location of PCRPID
newVal = checksum::crc32(-1, strBuf + pidLoc, loc - pidLoc); // calculating checksum over all the fields from table ID to the last stream element
updPos(188);
strBuf[loc + 3] = (newVal >> 24) & 0xFF;
strBuf[loc + 2] = (newVal >> 16) & 0xFF;
strBuf[loc + 1] = (newVal >> 8) & 0xFF;
strBuf[loc] = newVal & 0xFF;
memset((void *)(strBuf + loc + 4), 0xFF, 184 - loc);
}
/// Parses the PMT for streams, keeping track of their PIDs to make the Packet::isStream() function work
void ProgramMappingTable::parseStreams(){
ProgramMappingEntry entry = getEntry(0);
while (entry){
stream_pids[entry.getElementaryPid()] =
entry.getCodec() + std::string(" ") + entry.getStreamTypeString();
entry.advance();
}
}
/// Print all PMT values in a human readable format
///\param indent The indentation of the string printed as wanted by the user
///\return The string with human readable data from a PMT table
std::string ProgramMappingTable::toPrettyString(size_t indent) const{
std::stringstream output;
output << std::string(indent, ' ') << "[Program Mapping Table]" << std::endl;
output << std::string(indent + 2, ' ') << "Pointer Field: " << (int)getOffset() << std::endl;
output << std::string(indent + 2, ' ') << "Table ID: " << (int)getTableId() << std::endl;
output << std::string(indent + 2, ' ') << "Section Length: " << getSectionLength() << std::endl;
output << std::string(indent + 2, ' ') << "Program number: " << getProgramNumber() << std::endl;
output << std::string(indent + 2, ' ') << "Version number: " << (int)getVersionNumber() << std::endl;
output << std::string(indent + 2, ' ')
<< "Current next indicator: " << (int)getCurrentNextIndicator() << std::endl;
output << std::string(indent + 2, ' ') << "Section number: " << (int)getSectionNumber() << std::endl;
output << std::string(indent + 2, ' ') << "Last Section number: " << (int)getLastSectionNumber()
<< std::endl;
output << std::string(indent + 2, ' ') << "PCR PID: " << getPCRPID() << std::endl;
output << std::string(indent + 2, ' ') << "Program Info Length: " << getProgramInfoLength() << std::endl;
ProgramMappingEntry entry = getEntry(0);
while (entry){
output << std::string(indent + 4, ' ');
stream_pids[entry.getElementaryPid()] =
entry.getCodec() + std::string(" ") + entry.getStreamTypeString();
output << "Stream " << entry.getElementaryPid() << ": " << stream_pids[entry.getElementaryPid()]
<< " (" << entry.getStreamType() << ")" << std::endl;
output << ProgramDescriptors(entry.getESInfo(), entry.getESInfoLength()).toPrettyString(indent + 6);
entry.advance();
}
output << std::string(indent + 2, ' ') << "CRC32: " << std::hex << std::setw(8)
<< std::setfill('0') << std::uppercase << getCRC() << std::dec << std::endl;
return output.str();
}
ProgramDescriptors::ProgramDescriptors(const char *data, const uint32_t len)
: p_data(data), p_len(len){}
/// Returns the ISO-629 language code, or "und" if unknown.
std::string ProgramDescriptors::getLanguage() const{
for (uint32_t p = 0; p + 1 < p_len; p += p_data[p + 1] + 2){
if (p_data[p] == 0x0A){// language tag
// We assume the first one is the only interesting one
return std::string(p_data + p + 2, 3);
}
}
// No tag found! Undetermined by default.
return "und";
}
/// Prints all descriptors we understand in a readable format, the others in raw hex.
std::string ProgramDescriptors::toPrettyString(size_t indent) const{
std::stringstream output;
for (uint32_t p = 0; p + 1 < p_len; p += p_data[p + 1] + 2){
switch (p_data[p]){
case 0x0A:{// ISO 639-2 language tag (ISO 13818-1)
uint32_t offset = 0;
while (offset < p_data[p + 1]){// single language
output << std::string(indent, ' ') << "Language: " << std::string(p_data + p + 2 + offset, 3);
switch (p_data[p + 5 + offset]){
case 1: output << ", clean effects";
}
output << std::endl;
offset += 4;
}
}break;
case 0x48:{// DVB service descriptor
output << std::string(indent, ' ') << "DVB service: ";
uint32_t offset = p + 2;
switch (p_data[offset]){
case 0x01: output << "digital TV"; break;
case 0x02: output << "digital radio"; break;
case 0x10: output << "DVB MHP"; break;
default: output << "NOT IMPLEMENTED"; break;
}
offset++; // move to provider len
uint8_t len = p_data[offset];
output << ", Provider: " << std::string(p_data + offset + 1, len);
offset += 1 + len; // move to service len
len = p_data[offset];
output << ", Service: " << std::string(p_data + offset + 1, len) << std::endl;
}break;
case 0x7C:{// AAC descriptor (EN 300 468)
if (p_data[p + 1] < 2 || p + 2 + p_data[p + 1] > p_len){continue;}// skip broken data
output << std::string(indent, ' ') << "AAC profile: ";
switch (p_data[p + 2]){
case 0x50: output << "AAC, level 1"; break;
case 0x51: output << "AAC, level 2"; break;
case 0x52: output << "AAC, level 4"; break;
case 0x53: output << "AAC, level 5"; break;
case 0x58: output << "AAC-HE, level 2"; break;
case 0x59: output << "AAC-HE, level 3"; break;
case 0x5A: output << "AAC-HE, level 4"; break;
case 0x5B: output << "AAC-HE, level 5"; break;
case 0x60: output << "AAC-HEv2, level 2"; break;
case 0x61: output << "AAC-HEv2, level 3"; break;
case 0x62: output << "AAC-HEv2, level 4"; break;
case 0x63: output << "AAC-HEv2, level 5"; break;
default:
output << std::hex << std::setw(2) << std::setfill('0') << std::uppercase
<< (int)p_data[p + 2] << std::dec;
}
if (p_data[p + 3] & 0x80){
output << ", type: " << std::hex << std::setw(2) << std::setfill('0') << std::uppercase
<< (int)p_data[p + 3] << std::dec;
}
output << std::endl;
if (p_data[p + 1] > 2){
output << std::string(indent + 2, ' ') << "Extra data: ";
for (uint32_t offset = 2; p + 2 + offset < p_data[p + 1]; ++offset){
output << std::hex << std::setw(2) << std::setfill('0') << std::uppercase
<< (int)p_data[p + 2 + offset] << std::dec;
}
}
}break;
case 0x52:{// DVB stream identifier
output << std::string(indent, ' ') << "Stream identifier: Tag #" << (int)p_data[p + 2] << std::endl;
}break;
default:{
output << std::string(indent, ' ') << "Undecoded descriptor: ";
for (uint32_t i = 0; i < p_data[p + 1] + 2; ++i){
output << std::hex << std::setw(2) << std::setfill('0') << std::uppercase
<< (int)p_data[p + i] << std::dec;
}
output << std::endl;
}
}
}
return output.str();
}
/// Construct a PMT (special 188B ts packet) from a set of selected tracks and metadata.
/// This function is not part of the packet class, but it is in the TS namespace.
/// It uses an internal static TS packet for PMT storage.
///\param selectedTracks tracks to include in PMT creation
///\param myMeta
///\returns character pointer to a static 188B TS packet
const char *createPMT(std::set<unsigned long> &selectedTracks, DTSC::Meta &myMeta, int contCounter){
static ProgramMappingTable PMT;
PMT.setPID(4096);
PMT.setTableId(2);
// section length met 2 tracks: 0xB017
int sectionLen = 0;
for (std::set<long unsigned int>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
sectionLen += 5;
if (myMeta.tracks[*it].codec == "ID3"){sectionLen += myMeta.tracks[*it].init.size();}
if (myMeta.tracks[*it].codec == "AAC"){
sectionLen += 4; // aac descriptor
if (myMeta.tracks[*it].lang.size() == 3 && myMeta.tracks[*it].lang != "und"){
sectionLen += 6; // language descriptor
}
}
}
PMT.setSectionLength(0xB00D + sectionLen);
PMT.setProgramNumber(1);
PMT.setVersionNumber(0);
PMT.setCurrentNextIndicator(1);
PMT.setSectionNumber(0);
PMT.setLastSectionNumber(0);
PMT.setContinuityCounter(contCounter);
int vidTrack = -1;
for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
if (myMeta.tracks[*it].type == "video"){
vidTrack = *it;
break;
}
}
if (vidTrack == -1){vidTrack = *(selectedTracks.begin());}
PMT.setPCRPID(255 + vidTrack);
PMT.setProgramInfoLength(0);
short id = 0;
ProgramMappingEntry entry = PMT.getEntry(0);
for (std::set<long unsigned int>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
entry.setElementaryPid(255 + *it);
entry.setESInfo("");
if (myMeta.tracks[*it].codec == "H264"){
entry.setStreamType(0x1B);
}else if (myMeta.tracks[*it].codec == "HEVC"){
entry.setStreamType(0x24);
}else if (myMeta.tracks[*it].codec == "MPEG2"){
entry.setStreamType(0x02);
}else if (myMeta.tracks[*it].codec == "AAC"){
entry.setStreamType(0x0F);
std::string aac_info("\174\002\121\000", 4); // AAC descriptor: AAC Level 2. Hardcoded, because... what are AAC levels, anyway?
// language code ddescriptor
if (myMeta.tracks[*it].lang.size() == 3 && myMeta.tracks[*it].lang != "und"){
aac_info.append("\012\004", 2);
aac_info.append(myMeta.tracks[*it].lang);
aac_info.append("\000", 1);
}
entry.setESInfo(aac_info);
}else if (myMeta.tracks[*it].codec == "MP3" || myMeta.tracks[*it].codec == "MP2"){
entry.setStreamType(0x03);
}else if (myMeta.tracks[*it].codec == "AC3"){
entry.setStreamType(0x81);
}else if (myMeta.tracks[*it].codec == "ID3"){
entry.setStreamType(0x15);
entry.setESInfo(myMeta.tracks[*it].init);
}
entry.advance();
}
PMT.calcCRC();
return PMT.checkAndGetBuffer();
}
ServiceDescriptionEntry::ServiceDescriptionEntry(char *begin, char *end){
data = begin;
boundary = end;
}
ServiceDescriptionEntry::operator bool() const{return data && (data < boundary);}
uint16_t ServiceDescriptionEntry::getServiceID() const{return Bit::btohs(data);}
void ServiceDescriptionEntry::setServiceID(uint16_t val){Bit::htobs(data, val);}
bool ServiceDescriptionEntry::getEITSchedule() const{return data[2] & 2;}
void ServiceDescriptionEntry::setEITSchedule(bool val){data[2] |= 2;}
bool ServiceDescriptionEntry::getEITPresentFollowing() const{return data[2] & 1;}
void ServiceDescriptionEntry::setEITPresentFollowing(bool val){data[2] |= 1;}
uint8_t ServiceDescriptionEntry::getRunningStatus() const{return (data[3] & 0xE0) >> 5;}
void ServiceDescriptionEntry::setRunningStatus(uint8_t val){
data[3] = (data[3] & 0x1F) | (val << 5);
}
bool ServiceDescriptionEntry::getFreeCAM() const{return data[3] & 0x10;}
void ServiceDescriptionEntry::setFreeCAM(bool val){
data[3] = (data[3] & 0xEF) | (val ? 0x10 : 0);
}
int ServiceDescriptionEntry::getESInfoLength() const{
return ((data[3] << 8) | data[4]) & 0x0FFF;
}
const char *ServiceDescriptionEntry::getESInfo() const{return data + 5;}
void ServiceDescriptionEntry::setESInfo(const std::string &newInfo){
data[3] = ((newInfo.size() >> 8) & 0x0F) | (data[3] & 0xF0);
data[4] = newInfo.size() & 0xFF;
memcpy(data + 5, newInfo.data(), newInfo.size());
}
void ServiceDescriptionEntry::advance(){
if (!(*this)){return;}
data += 5 + getESInfoLength();
}
ServiceDescriptionTable::ServiceDescriptionTable(){
strBuf[0] = 0x47;
strBuf[1] = 0x50;
strBuf[2] = 0x00;
strBuf[3] = 0x10;
pos = 4;
}
ServiceDescriptionTable &ServiceDescriptionTable::operator=(const Packet &rhs){
memcpy(strBuf, rhs.checkAndGetBuffer(), 188);
pos = 188;
return *this;
}
char ServiceDescriptionTable::getOffset() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0);
return strBuf[loc];
}
void ServiceDescriptionTable::setOffset(char newVal){
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0);
strBuf[loc] = newVal;
}
char ServiceDescriptionTable::getTableId() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 1;
return strBuf[loc];
}
void ServiceDescriptionTable::setTableId(char newVal){
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 1;
updPos(loc + 1);
strBuf[loc] = newVal;
}
short ServiceDescriptionTable::getSectionLength() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 2;
return (((short)strBuf[loc] & 0x0F) << 8) | strBuf[loc + 1];
}
void ServiceDescriptionTable::setSectionLength(short newVal){
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 2;
updPos(loc + 2);
strBuf[loc] = (char)(newVal >> 8);
strBuf[loc + 1] = (char)newVal;
}
uint16_t ServiceDescriptionTable::getTSStreamID() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 4;
return (((short)strBuf[loc]) << 8) | strBuf[loc + 1];
}
void ServiceDescriptionTable::setTSStreamID(uint16_t newVal){
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 4;
updPos(loc + 2);
strBuf[loc] = (char)(newVal >> 8);
strBuf[loc + 1] = (char)newVal;
}
uint8_t ServiceDescriptionTable::getVersionNumber() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 6;
return (strBuf[loc] >> 1) & 0x1F;
}
void ServiceDescriptionTable::setVersionNumber(uint8_t newVal){
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 6;
updPos(loc + 1);
strBuf[loc] = ((newVal & 0x1F) << 1) | (strBuf[loc] & 0xC1);
}
/// Retrieves the "current/next" indicator
bool ServiceDescriptionTable::getCurrentNextIndicator() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 6;
return strBuf[loc] & 0x01;
}
/// Sets the "current/next" indicator
void ServiceDescriptionTable::setCurrentNextIndicator(bool newVal){
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 6;
updPos(loc + 1);
strBuf[loc] = (newVal ? 1 : 0) | (strBuf[loc] & 0xFE);
}
/// Retrieves the section number
uint8_t ServiceDescriptionTable::getSectionNumber() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 7;
return strBuf[loc];
}
/// Sets the section number
void ServiceDescriptionTable::setSectionNumber(uint8_t newVal){
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 7;
updPos(loc + 1);
strBuf[loc] = newVal;
}
/// Retrieves the last section number
uint8_t ServiceDescriptionTable::getLastSectionNumber() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 8;
return strBuf[loc];
}
/// Sets the last section number
void ServiceDescriptionTable::setLastSectionNumber(uint8_t newVal){
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 8;
updPos(loc + 1);
strBuf[loc] = newVal;
}
uint16_t ServiceDescriptionTable::getOrigID() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 9;
return (((short)strBuf[loc] & 0x1F) << 8) | strBuf[loc + 1];
}
void ServiceDescriptionTable::setOrigID(uint16_t newVal){
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 9;
updPos(loc + 2);
strBuf[loc] = (char)((newVal >> 8) & 0x1F) | 0xE0; // Note: here we set reserved bits on 1
strBuf[loc + 1] = (char)newVal;
}
ServiceDescriptionEntry ServiceDescriptionTable::getEntry(int index) const{
int dataOffset = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset();
ServiceDescriptionEntry res((char *)(strBuf + dataOffset + 12),
(char *)(strBuf + dataOffset + getSectionLength()));
for (int i = 0; i < index; i++){res.advance();}
return res;
}
int ServiceDescriptionTable::getCRC() const{
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) +
getOffset() + getSectionLength();
return ((int)(strBuf[loc]) << 24) | ((int)(strBuf[loc + 1]) << 16) |
((int)(strBuf[loc + 2]) << 8) | strBuf[loc + 3];
}
void ServiceDescriptionTable::calcCRC(){
unsigned int loc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) +
getOffset() + getSectionLength();
unsigned int newVal; // this will hold the CRC32 value;
unsigned int pidLoc = 4 + (getAdaptationField() > 1 ? getAdaptationFieldLen() + 1 : 0) + getOffset() + 1;
newVal = checksum::crc32(-1, strBuf + pidLoc, loc - pidLoc); // calculating checksum over all the fields from table ID to the last stream element
updPos(188);
strBuf[loc + 3] = (newVal >> 24) & 0xFF;
strBuf[loc + 2] = (newVal >> 16) & 0xFF;
strBuf[loc + 1] = (newVal >> 8) & 0xFF;
strBuf[loc] = newVal & 0xFF;
memset((void *)(strBuf + loc + 4), 0xFF, 184 - loc);
}
/// Print all SDT values in a human readable format
///\param indent The indentation of the string printed as wanted by the user
///\return The string with human readable data from a SDT table
std::string ServiceDescriptionTable::toPrettyString(size_t indent) const{
std::stringstream output;
output << std::string(indent, ' ') << "[Service Description Table]" << std::endl;
output << std::string(indent + 2, ' ') << "Pointer Field: " << (int)getOffset() << std::endl;
output << std::string(indent + 2, ' ') << "Table ID: " << (int)getTableId() << std::endl;
output << std::string(indent + 2, ' ') << "Section Length: " << getSectionLength() << std::endl;
output << std::string(indent + 2, ' ') << "TS Stream ID: " << getTSStreamID() << std::endl;
output << std::string(indent + 2, ' ') << "Version number: " << (int)getVersionNumber() << std::endl;
output << std::string(indent + 2, ' ')
<< "Current next indicator: " << (int)getCurrentNextIndicator() << std::endl;
output << std::string(indent + 2, ' ') << "Section number: " << (int)getSectionNumber() << std::endl;
output << std::string(indent + 2, ' ') << "Last Section number: " << (int)getLastSectionNumber()
<< std::endl;
output << std::string(indent + 2, ' ') << "Original Network ID: " << getOrigID() << std::endl;
ServiceDescriptionEntry entry = getEntry(0);
while (entry){
output << std::string(indent + 4, ' ');
output << "Service " << entry.getServiceID() << ":";
if (entry.getEITSchedule()){output << " EIT";}
if (entry.getEITPresentFollowing()){output << " EITPF";}
if (entry.getFreeCAM()){output << " Free";}
switch (entry.getRunningStatus()){
case 0: output << " Undefined"; break;
case 1: output << " NotRunning"; break;
case 2: output << " StartSoon"; break;
case 3: output << " Pausing"; break;
case 4: output << " Running"; break;
case 5: output << " OffAir"; break;
default: output << " UNIMPL?" << (int)entry.getRunningStatus(); break;
}
output << std::endl;
output << ProgramDescriptors(entry.getESInfo(), entry.getESInfoLength()).toPrettyString(indent + 6);
entry.advance();
}
output << std::string(indent + 2, ' ') << "CRC32: " << std::hex << std::setw(8)
<< std::setfill('0') << std::uppercase << getCRC() << std::dec << std::endl;
return output.str();
}
/// Construct a SDT from a set of selected tracks and metadata.
/// This function is not part of the packet class, but it is in the TS namespace.
/// It uses an internal static TS packet for SDT storage.
///\returns character pointer to a static 188B TS packet
const char *createSDT(const std::string &streamName, int contCounter){
static ServiceDescriptionTable SDT;
SDT.setPID(0x11);
SDT.setTableId(0x42);
SDT.setSectionLength(0x8020 + streamName.size());
SDT.setTSStreamID(1);
SDT.setVersionNumber(0);
SDT.setCurrentNextIndicator(1);
SDT.setSectionNumber(0);
SDT.setLastSectionNumber(0);
SDT.setOrigID(1);
ServiceDescriptionEntry entry = SDT.getEntry(0);
entry.setServiceID(1); // Same as ProgramNumber in PMT
entry.setRunningStatus(4); // Running
entry.setFreeCAM(true); // Not conditional access
std::string sdti;
sdti += (char)0x48;
sdti += (char)(15 + streamName.size()); // length
sdti += (char)1; // digital television service
sdti.append("\012MistServer");
sdti += (char)streamName.size();
sdti.append(streamName);
entry.setESInfo(sdti);
SDT.setContinuityCounter(contCounter);
SDT.calcCRC();
return SDT.checkAndGetBuffer();
}
}// namespace TS