mistserver/lib/ts_packet.cpp
2013-07-10 11:40:28 +02:00

439 lines
16 KiB
C++

/// \file ts_packet.cpp
/// Holds all code for the TS namespace.
#include <sstream>
#include "ts_packet.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
/// This constructor creates an empty TS::Packet, ready for use for either reading or writing.
/// All this constructor does is call TS::Packet::Clear().
TS::Packet::Packet(){
strBuf.reserve(188);
Clear();
}
/// This function fills a TS::Packet from provided Data.
/// It fills the content with the first 188 bytes of Data.
/// \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 TS::Packet::FromString(std::string & Data){
if (Data.size() < 188){
return false;
}else{
strBuf = Data.substr(0, 188);
Data.erase(0, 188);
}
return true;
}
/// The deconstructor deletes all space that may be occupied by a TS::Packet.
TS::Packet::~Packet(){
}
/// Sets the PID of a single TS::Packet.
/// \param NewPID The new PID of the packet.
void TS::Packet::PID(int NewPID){
strBuf[1] = (strBuf[1] & 0xE0) + ((NewPID & 0x1F00) >> 8);
strBuf[2] = (NewPID & 0x00FF);
}
/// Gets the PID of a single TS::Packet.
/// \return The value of the PID.
int TS::Packet::PID(){
return ((strBuf[1] & 0x1F) << 8) + strBuf[2];
}
/// Sets the Continuity Counter of a single TS::Packet.
/// \param NewContinuity The new Continuity Counter of the packet.
void TS::Packet::ContinuityCounter(int NewContinuity){
if (strBuf.size() < 4){
strBuf.resize(4);
}
strBuf[3] = (strBuf[3] & 0xF0) + (NewContinuity & 0x0F);
}
/// Gets the Continuity Counter of a single TS::Packet.
/// \return The value of the Continuity Counter.
int TS::Packet::ContinuityCounter(){
return (strBuf[3] & 0x0F);
}
/// Gets the amount of bytes that are not written yet in a TS::Packet.
/// \return The amount of bytes that can still be written to this packet.
int TS::Packet::BytesFree(){
return 188 - strBuf.size();
}
/// Clears a TS::Packet.
void TS::Packet::Clear(){
strBuf.resize(4);
strBuf[0] = 0x47;
strBuf[1] = 0x00;
strBuf[2] = 0x00;
strBuf[3] = 0x10;
}
/// Sets the selection value for an adaptationfield of a TS::Packet.
/// \param NewSelector The new value of the selection bits.
/// - 1: No AdaptationField.
/// - 2: AdaptationField Only.
/// - 3: AdaptationField followed by Data.
void TS::Packet::AdaptationField(int NewSelector){
strBuf[3] = (strBuf[3] & 0xCF) + ((NewSelector & 0x03) << 4);
if (NewSelector & 0x02){
strBuf[4] = 0x00;
}else{
strBuf.resize(4);
}
}
/// Gets whether a TS::Packet contains an adaptationfield.
/// \return The existence of an adaptationfield.
/// - 0: No adaptationfield present.
/// - 1: Adaptationfield is present.
int TS::Packet::AdaptationField(){
return ((strBuf[3] & 0x30) >> 4);
}
/// Sets the PCR (Program Clock Reference) of a TS::Packet.
/// \param NewVal The new PCR Value.
void TS::Packet::PCR(int64_t NewVal){
if (strBuf.size() < 12){
strBuf.resize(12);
}
AdaptationField(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 TS::Packet.
/// \return The value of the PCR.
int64_t TS::Packet::PCR(){
if ( !AdaptationField()){
return -1;
}
if ( !(strBuf[5] & 0x10)){
return -1;
}
int64_t Result = 0;
Result = (((strBuf[6] << 24) + (strBuf[7] << 16) + (strBuf[8] << 8) + strBuf[9]) << 1) + (strBuf[10] & 0x80 >> 7);
Result = Result * 300;
Result += ((strBuf[10] & 0x01) << 8 + strBuf[11]);
return Result;
}
/// Gets the current length of the adaptationfield.
/// \return The length of the adaptationfield.
int TS::Packet::AdaptationFieldLen(){
if ( !AdaptationField()){
return -1;
}
return (int)strBuf[4];
}
/// Prints a packet to stdout, for analyser purposes.
std::string TS::Packet::toPrettyString(size_t indent){
std::stringstream output;
output << std::string(indent,' ') << "TS Packet: " << (strBuf[0] == 0x47) << std::endl;
output << std::string(indent+2,' ') << "NewUnit: " << UnitStart() << std::endl;
output << std::string(indent+2,' ') << "PID: " << PID() << std::endl;
output << std::string(indent+2,' ') << "Continuity Counter: " << ContinuityCounter() << std::endl;
output << std::string(indent+2,' ') << "Adaption Field: " << AdaptationField() << std::endl;
if (AdaptationField()){
output << std::string(indent+4,' ') << "Adaptation Length: " << AdaptationFieldLen() << std::endl;;
if (AdaptationFieldLen()){
output << std::string(indent+4,' ') << "Random Access: " << RandomAccess() << std::endl;
}
if (PCR() != -1){
output << std::string(indent+4,' ') << "PCR: " << PCR() << "( " << (double)PCR() / 27000000 << " s )" << std::endl;
}
}
return output.str();
}
/// Gets whether a new unit starts in this TS::Packet.
/// \return The start of a new unit.
int TS::Packet::UnitStart(){
return (strBuf[1] & 0x40) >> 6;
}
/// Sets the start of a new unit in this TS::Packet.
/// \param NewVal The new value for the start of a unit.
void TS::Packet::UnitStart(int NewVal){
if (NewVal){
strBuf[1] |= 0x40;
}else{
strBuf[1] &= 0xBF;
}
}
/// Gets whether this TS::Packet can be accessed at random (indicates keyframe).
/// \return Whether or not this TS::Packet contains a keyframe.
int TS::Packet::RandomAccess(){
if (AdaptationField() < 2){
return -1;
}
return (strBuf[5] & 0x40) >> 6;
}
/// Sets whether this TS::Packet contains a keyframe
/// \param NewVal Whether or not this TS::Packet contains a keyframe.
void TS::Packet::RandomAccess(int NewVal){
if (AdaptationField() == 3){
if (strBuf.size() < 6){
strBuf.resize(6);
}
if ( !strBuf[4]){
strBuf[4] = 1;
}
if (NewVal){
strBuf[5] |= 0x40;
}else{
strBuf[5] &= 0xBF;
}
}else{
if (strBuf.size() < 6){
strBuf.resize(6);
}
AdaptationField(3);
strBuf[4] = 1;
if (NewVal){
strBuf[5] = 0x40;
}else{
strBuf[5] = 0x00;
}
}
}
/// Transforms the TS::Packet into a standard Program Association Table
void TS::Packet::DefaultPAT(){
static int MyCntr = 0;
strBuf = std::string(TS::PAT, 188);
ContinuityCounter(MyCntr++);
MyCntr %= 0x10;
}
/// Transforms the TS::Packet into a standard Program Mapping Table
void TS::Packet::DefaultPMT(){
static int MyCntr = 0;
strBuf = std::string(TS::PMT, 188);
ContinuityCounter(MyCntr++);
MyCntr %= 0x10;
}
/// Generates a string from the contents of the TS::Packet
/// \return A string representation of the packet.
const char* TS::Packet::ToString(){
if (strBuf.size() != 188){
std::cerr << "Error: Size invalid (" << strBuf.size() << ") Invalid data from this point on." << std::endl;
}
return strBuf.c_str();
}
/// Generates a PES Lead-in for a video frame.
/// Starts at the first Free byte.
/// \param NewLen The length of this frame.
/// \param PTS The timestamp of the frame.
void TS::Packet::PESVideoLeadIn(unsigned int NewLen, long long unsigned int PTS){
//NewLen += 19;
NewLen = 0;
strBuf += (char)0x00; //PacketStartCodePrefix
strBuf += (char)0x00; //PacketStartCodePrefix (Cont)
strBuf += (char)0x01; //PacketStartCodePrefix (Cont)
strBuf += (char)0xe0; //StreamType Video
strBuf += (char)((NewLen & 0xFF00) >> 8); //PES PacketLength
strBuf += (char)(NewLen & 0x00FF); //PES PacketLength (Cont)
strBuf += (char)0x84; //Reserved + Flags
strBuf += (char)0xC0; //PTSOnlyFlag + Flags
strBuf += (char)0x0A; //PESHeaderDataLength
strBuf += (char)(0x30 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + PTS
strBuf += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont)
strBuf += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont)
strBuf += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont)
strBuf += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont)
strBuf += (char)(0x10 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + DTS
strBuf += (char)((PTS & 0x03FC00000LL) >> 22); //DTS (Cont)
strBuf += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //DTS (Cont)
strBuf += (char)((PTS & 0x000007F80LL) >> 7); //DTS (Cont)
strBuf += (char)(((PTS & 0x00000007FLL) << 1) + 1); //DTS (Cont)
//PesPacket-Wise Prepended Data
strBuf += (char)0x00; //NALU StartCode
strBuf += (char)0x00; //NALU StartCode (Cont)
strBuf += (char)0x00; //NALU StartCode (Cont)
strBuf += (char)0x01; //NALU StartCode (Cont)
strBuf += (char)0x09; //NALU EndOfPacket (Einde Vorige Packet)
strBuf += (char)0xF0; //NALU EndOfPacket (Cont)
}
/// Generates a PES Lead-in for an audio frame.
/// Starts at the first Free byte.
/// \param NewLen The length of this frame.
/// \param PTS The timestamp of the frame.
void TS::Packet::PESAudioLeadIn(unsigned int NewLen, uint64_t PTS){
NewLen += 5;
strBuf += (char)0x00; //PacketStartCodePrefix
strBuf += (char)0x00; //PacketStartCodePrefix (Cont)
strBuf += (char)0x01; //PacketStartCodePrefix (Cont)
strBuf += (char)0xc0; //StreamType Audio
strBuf += (char)((NewLen & 0xFF00) >> 8); //PES PacketLength
strBuf += (char)(NewLen & 0x00FF); //PES PacketLength (Cont)
strBuf += (char)0x84; //Reserved + Flags
strBuf += (char)0x80; //PTSOnlyFlag + Flags
strBuf += (char)0x05; //PESHeaderDataLength
strBuf += (char)(0x30 + ((PTS & 0x1C0000000LL) >> 29) + 1); //PTS
strBuf += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont)
strBuf += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont)
strBuf += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont)
strBuf += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont)
}
/// 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 toSend Data that is to be send, will be modified.
/// \param PTS The timestamp of the frame.
void TS::Packet::PESVideoLeadIn(std::string & toSend, long long unsigned int PTS){
std::string tmpStr;
tmpStr.reserve(25);
tmpStr.append("\000\000\001\340\000\000\204\300\012", 9);
tmpStr += (char)(0x30 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + PTS
tmpStr += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont)
tmpStr += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont)
tmpStr += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont)
tmpStr += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont)
tmpStr += (char)(0x10 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + DTS
tmpStr += (char)((PTS & 0x03FC00000LL) >> 22); //DTS (Cont)
tmpStr += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //DTS (Cont)
tmpStr += (char)((PTS & 0x000007F80LL) >> 7); //DTS (Cont)
tmpStr += (char)(((PTS & 0x00000007FLL) << 1) + 1); //DTS (Cont)
tmpStr.append("\000\000\000\001\011\360", 6);
toSend.insert(0, 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 toSend Data that is to be send, will be modified.
/// \param PTS The timestamp of the frame.
void TS::Packet::PESAudioLeadIn(std::string & toSend, long long unsigned int PTS){
std::string tmpStr;
tmpStr.reserve(14);
unsigned int NewLen = toSend.size() + 5;
tmpStr.append("\000\000\001\300", 4);
tmpStr += (char)((NewLen & 0xFF00) >> 8); //PES PacketLength
tmpStr += (char)(NewLen & 0x00FF); //PES PacketLength (Cont)
tmpStr.append("\204\200\005", 3);
tmpStr += (char)(0x30 + ((PTS & 0x1C0000000LL) >> 29) + 1); //PTS
tmpStr += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont)
tmpStr += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont)
tmpStr += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont)
tmpStr += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont)
toSend.insert(0, tmpStr);
}
/// 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 NewLen The length of this frame.
/// \param PTS The timestamp of the frame.
std::string & TS::Packet::getPESVideoLeadIn(unsigned int NewLen, long long unsigned int PTS){
static std::string tmpStr;
tmpStr.clear();
tmpStr.reserve(25);
tmpStr.append("\000\000\001\340\000\000\204\300\012", 9);
tmpStr += (char)(0x30 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + PTS
tmpStr += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont)
tmpStr += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont)
tmpStr += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont)
tmpStr += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont)
tmpStr += (char)(0x10 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + DTS
tmpStr += (char)((PTS & 0x03FC00000LL) >> 22); //DTS (Cont)
tmpStr += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //DTS (Cont)
tmpStr += (char)((PTS & 0x000007F80LL) >> 7); //DTS (Cont)
tmpStr += (char)(((PTS & 0x00000007FLL) << 1) + 1); //DTS (Cont)
tmpStr.append("\000\000\000\001\011\360", 6);
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 NewLen The length of this frame.
/// \param PTS The timestamp of the frame.
std::string & TS::Packet::getPESAudioLeadIn(unsigned int NewLen, long long unsigned int PTS){
static std::string tmpStr;
tmpStr.clear();
tmpStr.reserve(14);
NewLen = NewLen + 8;
tmpStr.append("\000\000\001\300", 4);
tmpStr += (char)((NewLen & 0xFF00) >> 8); //PES PacketLength
tmpStr += (char)(NewLen & 0x00FF); //PES PacketLength (Cont)
tmpStr.append("\204\200\005", 3);
tmpStr += (char)(0x20 + ((PTS & 0x1C0000000LL) >> 29) + 1); //PTS
tmpStr += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont)
tmpStr += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont)
tmpStr += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont)
tmpStr += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont)
return tmpStr;
}
/// Fills the free bytes of the TS::Packet.
/// Stores as many bytes from NewVal as possible in the packet.
/// \param NewVal The data to store in the packet.
void TS::Packet::FillFree(std::string & NewVal){
int toWrite = BytesFree();
if (toWrite == NewVal.size()){
strBuf += NewVal;
NewVal.clear();
}else{
strBuf += NewVal.substr(0, toWrite);
NewVal.erase(0, toWrite);
}
}
/// Fills the free bytes of the TS::Packet.
/// Stores as many bytes from NewVal as possible in the packet.
/// \param NewVal The data to store in the packet.
int TS::Packet::FillFree(const char* NewVal, int maxLen){
int toWrite = std::min((int)BytesFree(), maxLen);
strBuf += std::string(NewVal, toWrite);
return toWrite;
}
/// Adds stuffing to the TS::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.
unsigned int TS::Packet::AddStuffing(int NumBytes){
if (BytesFree() <= NumBytes){
return BytesFree();
}
NumBytes = BytesFree() - NumBytes;
if (AdaptationField() == 3){
strBuf.resize(5 + strBuf[4]);
strBuf[4] += NumBytes;
for (int i = 0; i < NumBytes; i++){
strBuf.append(FILLER_DATA + (i % sizeof(FILLER_DATA)), 1);
}
}else{
AdaptationField(3);
if (NumBytes > 1){
strBuf.resize(6);
strBuf[4] = (char)(NumBytes - 1);
strBuf[5] = (char)0x00;
for (int i = 0; i < (NumBytes - 2); i++){
strBuf += FILLER_DATA[i % sizeof(FILLER_DATA)];
}
}else{
strBuf.resize(5);
strBuf[4] = (char)(NumBytes - 1);
}
}
return BytesFree();
}