/// \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

/// A standard Program Association Table, as generated by FFMPEG.
/// Seems to be independent of the stream.
// 0x47 = sync byte
// 0x4000 = transport error(1) = 0, payload unit start(1) = 1, priority(1) = 0, PID(13) = 0
// 0x10 = transportscrambling(2) = 0, adaptation(2) = 1, continuity(4) = 0
// 0x00 = pointer = 0
// 0x00 = table ID = 0 = PAT
// 0xB00D = section syntax(1) = 1, 0(1)=0, reserved(2) = 3, section_len(12) = 13
// 0x0001 = transport stream id = 1
// 0xC1 = reserved(2) = 3, version(5)=0, curr_next_indi(1) = 1
// 0x00 = section_number = 0
// 0x00 = last_section_no = 0
// 0x0001 = ProgNo = 1
// 0xF000 = reserved(3) = 7, network pid = 4096
// 0x2AB104B2 = CRC32

namespace TS{
  std::map<unsigned int, std::string> stream_pids;

  char PAT[188] ={
      0x47, 0x40, 0x00, 0x10, 0x00, 0x00, 0xB0, 0x0D, 0x00, 0x01, 0xC1, 0x00, 0x00, 0x00, 0x01,
      0xF0, 0x00, 0x2A, 0xB1, 0x04, 0xB2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

  /// 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 strBuf[0] == 0x47;
  }

  /// 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(const std::set<unsigned int> &pidlist, 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(pidlist)){
        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 (pidlist.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 std::set<unsigned int> & pidList) const{return pidList.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.
  void Packet::getPESVideoLeadIn(std::string &outData, 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;
    }

    outData.append("\000\000\001\340", 4);
    outData += (char)((len >> 8) & 0xFF);
    outData += (char)(len & 0xFF);
    if (isAligned){
      outData.append("\204", 1);
    }else{
      outData.append("\200", 1);
    }
    outData += (char)((offset ? 0xC0 : 0x80) | (bps ? 0x10 : 0)); // PTS/DTS + Flags
    outData += (char)((offset ? 10 : 5) + (bps ? 3 : 0));         // PESHeaderDataLength
    encodePESTimestamp(outData, (offset ? 0x30 : 0x20), PTS + offset);
    if (offset){encodePESTimestamp(outData, 0x10, PTS);}
    if (bps){
      char rate_buf[3];
      Bit::htob24(rate_buf, (bps / 50) | 0x800001);
      outData.append(rate_buf, 3);
    }
  }


  /// 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.
  void Packet::getPESAudioLeadIn(std::string & outData, unsigned int len, unsigned long long PTS, uint64_t bps){
    if (bps >= 50){
      len += 3;
    }else{
      bps = 0;
    }

    len += 8;
    outData.append("\000\000\001\300", 4);
    outData += (char)((len & 0xFF00) >> 8);     // PES PacketLength
    outData += (char)(len & 0x00FF);            // PES PacketLength (Cont)
    outData += (char)0x84;                      // isAligned
    outData += (char)(0x80 | (bps ? 0x10 : 0)); // PTS/DTS + Flags
    outData += (char)(5 + (bps ? 3 : 0));       // PESHeaderDataLength
    encodePESTimestamp(outData, 0x20, PTS);
    if (bps){
      char rate_buf[3];
      Bit::htob24(rate_buf, (bps / 50) | 0x800001);
      outData.append(rate_buf, 3);
    }
  }

  /// 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;
  }

  /// Generates a PES Lead-in for a meta 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::getPESMetaLeadIn(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\374", 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;
  }

  /// Generates a PES Lead-in for a meta 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::getPESPS1LeadIn(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\275", 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(std::set<unsigned int> & pidlist){
    for (int i = 0; i < getProgramCount(); i++){pidlist.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";
  }

  /// Returns the registration field, or an empty string if none present.
  std::string ProgramDescriptors::getRegistration() const{
    for (uint32_t p = 0; p + 1 < p_len; p += p_data[p + 1] + 2){
      if (p_data[p] == 0x05){// registration
        return std::string(p_data + p + 2, 4);
      }
    }
    // No tag found! Empty string.
    return "";
  }

  /// Returns the extension field, or an empty string if none present.
  std::string ProgramDescriptors::getExtension() const{
    for (uint32_t p = 0; p + 1 < p_len; p += p_data[p + 1] + 2){
      if (p_data[p] == 0x7F){// extension
        return std::string(p_data + p + 2, p_data[p+1]);
      }
    }
    // No tag found! Empty string.
    return "";
  }

  /// 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 0x05:{// registration descriptor
        if ((p_data[p+2] >= 32 && p_data[p+2] <= 126) &&
            (p_data[p+2] >= 32 && p_data[p+2] <= 126) &&
            (p_data[p+2] >= 32 && p_data[p+2] <= 126) &&
            (p_data[p+2] >= 32 && p_data[p+2] <= 126)){
          //printable ASCII
          output << std::string(indent, ' ') << "Registration: " << std::string(p_data + p + 2, 4);
          //Extra binary data, if any
          if (p_data[p+1] > 4){
            output << " (";
            for (uint32_t i = 6; 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 << ")";
          }
          output << std::endl;
        }else{
          output << std::string(indent, ' ') << "Registration (unprintable): ";
          for (uint32_t i = 2; 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;
        }
      }break;
      case 0x06:{// data alignment descriptor
        output << std::string(indent, ' ') << "Data alignment: ";
        switch (p_data[p+2]){
          case 1: output << "Slice or Video Access Unit (1)"; break;
          case 2: output << "Video Access Unit (2)"; break;
          case 3: output << "GOP or SEQ (3)"; break;
          case 4: output << "SEQ (4)"; break;
          default: output << "Reserved (" << (int)(p_data[p+2]) << ")"; break;
        }
        output << std::endl;
      }break;
      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 0x56:{// DVB teletext descriptor
        output << std::string(indent, ' ') << "DVB teletext:" << std::endl;
        uint32_t len = p_data[p+1];
        uint32_t offset = p + 2;
        while (offset < p+2+len){
          output << std::string(indent+2, ' ') << "Language: " << std::string(p_data + offset, 3) << ", ";
          switch ((p_data[offset+3] & 0x78) >> 3){
          case 0x01: output << "initial, "; break;
          case 0x02: output << "subtitle, "; break;
          case 0x03: output << "additional info, "; break;
          case 0x04: output << "programme schedule, "; break;
          case 0x05: output << "hearing impaired subtitle, "; break;
          default: output << "reserved, "; break;
          }
          output << "magazine " << (int)(p_data[offset+3] & 0x7) << ", ";
          output << "page " << (int)p_data[offset+4] << std::endl;
          offset += 5;
        }
      }break;
      case 0x59:{// DVB subtitling descriptor
        output << std::string(indent, ' ') << "DVB subtitle:" << std::endl;
        uint32_t len = p_data[p+1];
        uint32_t offset = p + 2;
        while (offset < p+2+len){
          output << std::string(indent+2, ' ') << "Language: " << std::string(p_data + offset, 3) << ", ";
          output << "type " << (int)p_data[offset+3] << ", ";
          output << "composition " << Bit::btohs(p_data+offset+4) << ", ";
          output << "ancillary " << Bit::btohs(p_data+offset+6) << std::endl;
          offset += 8;
        }
      }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 0x7F:{// extension descriptor
        output << std::string(indent, ' ') << "Extension: ";
        if (p_data[p+2] < 0x80){
          output << "Unimplemented";
        }else{
          output << "User defined";
        }
        output << " (";
        output << std::hex << std::setw(2) << std::setfill('0') << std::uppercase << (int)p_data[p + 2] << std::dec;
        output << ") - ";
        for (uint32_t i = 3; 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;
      }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();
  }


  size_t getUniqTrackID(const DTSC::Meta &M, size_t idx){
    return idx+255;
    //size_t ret = M.getID(idx);
    //if (ret < 255){ret += 255;}
    //return ret;
  }

  /// 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<size_t> &selectedTracks, const DTSC::Meta &M, int contCounter){
    static ProgramMappingTable PMT;
    PMT.setPID(4096);
    PMT.setTableId(2);
    // section length met 2 tracks: 0xB017
    int sectionLen = 0;
    for (std::set<size_t>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
      std::string codec = M.getCodec(*it);
      sectionLen += 5;
      if (codec == "ID3" || codec == "RAW"){sectionLen += M.getInit(*it).size();}
      if (codec == "AAC"){sectionLen += 4;} // length of AAC descriptor
      if (codec == "opus"){sectionLen += 10;} // 6 bytes registration desc, 4 bytes opus desc
      std::string lang = M.getLang(*it);
      if (lang.size() == 3 && 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<size_t>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
      if (M.getType(*it) == "video"){
        vidTrack = *it;
        break;
      }
    }
    if (vidTrack == -1){vidTrack = *(selectedTracks.begin());}
    PMT.setPCRPID(getUniqTrackID(M, vidTrack));
    PMT.setProgramInfoLength(0);
    ProgramMappingEntry entry = PMT.getEntry(0);
    for (std::set<size_t>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
      std::string codec = M.getCodec(*it);
      entry.setElementaryPid(getUniqTrackID(M, *it));
      std::string es_info;
      if (codec == "H264"){
        entry.setStreamType(0x1B);
      }else if (codec == "HEVC"){
        entry.setStreamType(0x24);
      }else if (codec == "MPEG2"){
        entry.setStreamType(0x02);
      }else if (codec == "AAC"){
        entry.setStreamType(0x0F);
        // AAC descriptor: AAC Level 2. Hardcoded, because... what are AAC levels, anyway?
        es_info.append("\174\002\121\000", 4);
      }else if (codec == "MP3" || codec == "MP2"){
        entry.setStreamType(0x03);
      }else if (codec == "opus"){
        entry.setStreamType(0x06);
        es_info.append("\005\004Opus", 6);//registration descriptor
        es_info.append("\177\002\200", 3);//Opus descriptor
        es_info.append(1, (char)M.getChannels(*it));
      }else if (codec == "AC3"){
        entry.setStreamType(0x81);
      }else if (codec == "ID3"){
        entry.setStreamType(0x15);
        entry.setESInfo(M.getInit(*it));
      }else if (codec == "RAW"){
        entry.setStreamType(0x06);
        entry.setESInfo(M.getInit(*it));
      }
      // language code descriptor
      std::string lang = M.getLang(*it);
      if (lang.size() == 3 && lang != "und"){
        es_info.append("\012\004", 2);
        es_info.append(lang);
        es_info.append("\000", 1);
      }
      entry.setESInfo(es_info);
      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)(4 + strlen(APPNAME) + streamName.size()); // length
    sdti += (char)1;                        // digital television service
    sdti += (char)strlen(APPNAME);
    sdti.append(APPNAME);
    sdti += (char)streamName.size();
    sdti.append(streamName);
    entry.setESInfo(sdti);
    SDT.setContinuityCounter(contCounter);
    SDT.calcCRC();
    return SDT.checkAndGetBuffer();
  }

}// namespace TS