Initial TS Input commit
This commit is contained in:
parent
10f0f6bb92
commit
1f4b523b1b
33 changed files with 1300 additions and 643 deletions
123
lib/adts.cpp
Normal file
123
lib/adts.cpp
Normal file
|
@ -0,0 +1,123 @@
|
|||
#include "adts.h"
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "defines.h"
|
||||
|
||||
namespace aac {
|
||||
adts::adts(){
|
||||
data = NULL;
|
||||
len = 0;
|
||||
}
|
||||
|
||||
adts::adts(char * _data, unsigned long _len){
|
||||
len = _len;
|
||||
data = (char*)malloc(len);
|
||||
memcpy(data, _data, len);
|
||||
}
|
||||
|
||||
adts::adts(const adts & rhs){
|
||||
*this = rhs;
|
||||
}
|
||||
|
||||
adts& adts::operator = (const adts & rhs){
|
||||
len = rhs.len;
|
||||
data = (char*)malloc(len);
|
||||
memcpy(data, rhs.data, len);
|
||||
return * this;
|
||||
}
|
||||
|
||||
adts::~adts(){
|
||||
if (data){
|
||||
free(data);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long adts::getAACProfile(){
|
||||
if (!data || !len){
|
||||
return 0;
|
||||
}
|
||||
return ((data[2] >> 6) & 0x03) + 1;
|
||||
}
|
||||
|
||||
unsigned long adts::getFrequencyIndex(){
|
||||
if (!data || !len){
|
||||
return 0;
|
||||
}
|
||||
return ((data[2] >> 2) & 0x0F);
|
||||
|
||||
}
|
||||
|
||||
unsigned long adts::getFrequency(){
|
||||
if (!data || !len){
|
||||
return 0;
|
||||
}
|
||||
switch(getFrequencyIndex()){
|
||||
case 0: return 96000; break;
|
||||
case 1: return 88200; break;
|
||||
case 2: return 64000; break;
|
||||
case 3: return 48000; break;
|
||||
case 4: return 44100; break;
|
||||
case 5: return 32000; break;
|
||||
case 6: return 24000; break;
|
||||
case 7: return 22050; break;
|
||||
case 8: return 16000; break;
|
||||
case 9: return 12000; break;
|
||||
case 10: return 11025; break;
|
||||
case 11: return 8000; break;
|
||||
case 12: return 7350; break;
|
||||
default: return 0; break;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long adts::getChannelConfig(){
|
||||
if (!data || !len){
|
||||
return 0;
|
||||
}
|
||||
return ((data[2] & 0x01) << 2) | ((data[3] >> 6) & 0x03);
|
||||
}
|
||||
|
||||
unsigned long adts::getChannelCount(){
|
||||
if (!data || !len){
|
||||
return 0;
|
||||
}
|
||||
return (getChannelConfig() == 7 ? 8 : getChannelConfig());
|
||||
}
|
||||
|
||||
unsigned long adts::getHeaderSize(){
|
||||
if (!data || !len){
|
||||
return 0;
|
||||
}
|
||||
return (data[1] & 0x01 ? 7 : 9);
|
||||
}
|
||||
|
||||
unsigned long adts::getPayloadSize(){
|
||||
if (!data || !len){
|
||||
return 0;
|
||||
}
|
||||
return (((data[3] & 0x03) << 11) | (data[4] << 3) | ((data[5] >> 5) & 0x07)) - getHeaderSize();
|
||||
}
|
||||
|
||||
unsigned long adts::getSampleCount(){
|
||||
if (!data || !len){
|
||||
return 0;
|
||||
}
|
||||
return ((data[6] & 0x03) + 1) * 1024;//Number of samples in this frame * 1024
|
||||
}
|
||||
|
||||
char * adts::getPayload() {
|
||||
if (!data || !len){
|
||||
return 0;
|
||||
}
|
||||
return data + getHeaderSize();
|
||||
}
|
||||
std::string adts::toPrettyString(){
|
||||
std::stringstream res;
|
||||
res << "SyncWord: " << std::hex << (((int)data[0] << 4) | ((data[1] >> 4) & 0x0F)) << std::endl;
|
||||
res << "HeaderSize: " << std::dec << getHeaderSize() << std::endl;
|
||||
res << "PayloadSize: " << std::dec << getPayloadSize() << std::endl;
|
||||
return res.str();
|
||||
}
|
||||
}
|
25
lib/adts.h
Normal file
25
lib/adts.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
#include <string>
|
||||
|
||||
namespace aac {
|
||||
class adts {
|
||||
public:
|
||||
adts();
|
||||
adts(char * _data, unsigned long _len);
|
||||
adts(const adts & rhs);
|
||||
~adts();
|
||||
adts& operator = (const adts & rhs);
|
||||
unsigned long getAACProfile();
|
||||
unsigned long getFrequencyIndex();
|
||||
unsigned long getFrequency();
|
||||
unsigned long getChannelConfig();
|
||||
unsigned long getChannelCount();
|
||||
unsigned long getHeaderSize();
|
||||
unsigned long getPayloadSize();
|
||||
unsigned long getSampleCount();
|
||||
char * getPayload();
|
||||
std::string toPrettyString();
|
||||
private:
|
||||
char * data;
|
||||
unsigned long len;
|
||||
};
|
||||
}
|
|
@ -26,7 +26,7 @@ namespace Utils {
|
|||
}
|
||||
}
|
||||
|
||||
void bitstream::append(char * input, size_t bytes) {
|
||||
void bitstream::append(const char * input, size_t bytes) {
|
||||
if (checkBufferSize(dataSize + bytes)) {
|
||||
memcpy(data + dataSize, input, bytes);
|
||||
dataSize += bytes;
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace Utils {
|
|||
append(std::string(input, 1));
|
||||
return *this;
|
||||
};
|
||||
void append(char * input, size_t bytes);
|
||||
void append(const char * input, size_t bytes);
|
||||
void append(std::string input);
|
||||
long long unsigned int size();
|
||||
void skip(size_t count);
|
||||
|
|
|
@ -108,7 +108,7 @@ namespace DTSC {
|
|||
operator bool() const;
|
||||
packType getVersion() const;
|
||||
void reInit(const char * data_, unsigned int len, bool noCopy = false);
|
||||
void genericFill(long long packTime, long long packOffset, long long packTrack, char * packData, long long packDataSize, long long packBytePos, bool isKeyframe);
|
||||
void genericFill(long long packTime, long long packOffset, long long packTrack, const char * packData, long long packDataSize, long long packBytePos, bool isKeyframe);
|
||||
void getString(const char * identifier, char *& result, unsigned int & len) const;
|
||||
void getString(const char * identifier, std::string & result) const;
|
||||
void getInt(const char * identifier, int & result) const;
|
||||
|
|
|
@ -19,7 +19,10 @@ namespace DTSC {
|
|||
|
||||
/// Copy constructor for packets, copies an existing packet with same noCopy flag as original.
|
||||
Packet::Packet(const Packet & rhs) {
|
||||
Packet(rhs.data, rhs.dataLen, !rhs.master);
|
||||
master = false;
|
||||
bufferLen = 0;
|
||||
data = NULL;
|
||||
reInit(rhs.data, rhs.dataLen, !rhs.master);
|
||||
}
|
||||
|
||||
/// Data constructor for packets, either references or copies a packet from raw data.
|
||||
|
@ -112,7 +115,7 @@ namespace DTSC {
|
|||
///\param noCopy Determines whether to make a copy or not
|
||||
void Packet::reInit(const char * data_, unsigned int len, bool noCopy) {
|
||||
if (!data_) {
|
||||
DEBUG_MSG(DLVL_DEVEL, "ReInit received a null pointer with len %d, ignoring", len);
|
||||
HIGH_MSG("ReInit received a null pointer with len %d, ignoring", len);
|
||||
null();
|
||||
return;
|
||||
}
|
||||
|
@ -168,7 +171,8 @@ namespace DTSC {
|
|||
}
|
||||
|
||||
/// Re-initializes this Packet to contain a generic DTSC packet with the given data fields.
|
||||
void Packet::genericFill(long long packTime, long long packOffset, long long packTrack, char * packData, long long packDataSize, long long packBytePos, bool isKeyframe){
|
||||
/// When given a NULL pointer, the data is reserved and memset to 0
|
||||
void Packet::genericFill(long long packTime, long long packOffset, long long packTrack, const char * packData, long long packDataSize, long long packBytePos, bool isKeyframe){
|
||||
null();
|
||||
master = true;
|
||||
//time and trackID are part of the 20-byte header.
|
||||
|
@ -217,7 +221,11 @@ namespace DTSC {
|
|||
memcpy(data+offset, "\000\004data\002", 7);
|
||||
tmpLong = htonl(packDataSize);
|
||||
memcpy(data+offset+7, (char *)&tmpLong, 4);
|
||||
memcpy(data+offset+11, packData, packDataSize);
|
||||
if (packData){
|
||||
memcpy(data+offset+11, packData, packDataSize);
|
||||
}else{
|
||||
memset(data+offset+11, 0, packDataSize);
|
||||
}
|
||||
//finish container with 0x0000EE
|
||||
memcpy(data+offset+11+packDataSize, "\000\000\356", 3);
|
||||
}
|
||||
|
|
71
lib/h264.cpp
Normal file
71
lib/h264.cpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
#include "h264.h"
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include "bitfields.h"
|
||||
#include "defines.h"
|
||||
|
||||
namespace h264 {
|
||||
unsigned long toAnnexB(const char * data, unsigned long dataSize, char *& result){
|
||||
//toAnnexB keeps the same size.
|
||||
if (!result){
|
||||
result = (char *)malloc(dataSize);
|
||||
}
|
||||
int offset = 0;
|
||||
while (offset < dataSize){
|
||||
//Read unit size
|
||||
unsigned long unitSize = Bit::btohl(data + offset);
|
||||
//Write annex b header
|
||||
memset(result + offset, 0x00, 3);
|
||||
result[offset + 3] = 0x01;
|
||||
//Copy the nal unit
|
||||
memcpy(result + offset + 4, data + offset + 4, unitSize);
|
||||
//Update the offset
|
||||
offset += 4 + unitSize;
|
||||
}
|
||||
return dataSize;
|
||||
}
|
||||
|
||||
unsigned long fromAnnexB(const char * data, unsigned long dataSize, char *& result){
|
||||
if (!result){
|
||||
//first compute the new size. This might be the same as the annex b version, but this is not guaranteed
|
||||
int offset = 0;
|
||||
int newSize = 0;
|
||||
while (offset < dataSize){
|
||||
const char * begin = (const char*)memmem(data + offset, dataSize - offset, "\000\000\001", 3);
|
||||
begin += 3;//Initialize begin after the first 0x000001 pattern.
|
||||
const char * end = (const char*)memmem(begin, dataSize - (begin - data), "\000\000\001", 3);
|
||||
if (end - data > dataSize){
|
||||
end = data + dataSize;
|
||||
}
|
||||
//Check for 4-byte lead in's. Yes, we access -1 here
|
||||
if (end[-1] == 0x00){
|
||||
end--;
|
||||
}
|
||||
newSize += 4 + (end - begin);//end - begin = nalSize
|
||||
offset = end - data;
|
||||
}
|
||||
result = (char *)malloc(newSize);
|
||||
}
|
||||
int offset = 0;
|
||||
int newOffset = 0;
|
||||
while (offset < dataSize){
|
||||
const char * begin = ((const char*)memmem(data + offset, dataSize - offset, "\000\000\001", 3)) + 3;//Initialize begin after the first 0x000001 pattern.
|
||||
const char * end = (const char*)memmem(begin, dataSize - (begin - data), "\000\000\001", 3);
|
||||
if (end - data > dataSize){
|
||||
end = data + dataSize;
|
||||
}
|
||||
//Check for 4-byte lead in's. Yes, we access -1 here
|
||||
if (end[-1] == 0x00){
|
||||
end--;
|
||||
}
|
||||
unsigned int nalSize = end - begin;
|
||||
Bit::htobl(result + newOffset, nalSize);
|
||||
memcpy(result + newOffset + 4, begin, nalSize);
|
||||
|
||||
newOffset += 4 + nalSize;
|
||||
offset = end - data;
|
||||
}
|
||||
return newOffset;
|
||||
}
|
||||
}
|
||||
|
4
lib/h264.h
Normal file
4
lib/h264.h
Normal file
|
@ -0,0 +1,4 @@
|
|||
namespace h264 {
|
||||
unsigned long toAnnexB(const char * data, unsigned long dataSize, char *& result);
|
||||
unsigned long fromAnnexB(const char * data, unsigned long dataSize, char *& result);
|
||||
}
|
120
lib/nal.cpp
120
lib/nal.cpp
|
@ -21,6 +21,20 @@ namespace h264 {
|
|||
return result;
|
||||
}
|
||||
|
||||
std::deque<nalData> analyseH264Packet(const char * data, unsigned long len){
|
||||
std::deque<nalData> res;
|
||||
|
||||
int offset = 0;
|
||||
while (offset < len){
|
||||
nalData entry;
|
||||
entry.nalSize = Bit::btohl(data + offset);
|
||||
entry.nalType = (data + offset)[4] & 0x1F;
|
||||
res.push_back(entry);
|
||||
offset += entry.nalSize + 4;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
///empty constructor of NAL
|
||||
NAL::NAL() {
|
||||
|
||||
|
@ -518,5 +532,111 @@ namespace h264 {
|
|||
std::cout << "second_chroma_qp_index_offset: " << bs.getExpGolomb() << std::endl;
|
||||
}
|
||||
|
||||
sequenceParameterSet::sequenceParameterSet(const char * _data, unsigned long _dataLen) : data(_data), dataLen(_dataLen) {}
|
||||
|
||||
SPSMeta sequenceParameterSet::getCharacteristics() const {
|
||||
SPSMeta result;
|
||||
|
||||
//For calculating width
|
||||
unsigned int widthInMbs = 0;
|
||||
unsigned int cropHorizontal = 0;
|
||||
|
||||
//For calculating height
|
||||
bool mbsOnlyFlag = 0;
|
||||
unsigned int heightInMapUnits = 0;
|
||||
unsigned int cropVertical = 0;
|
||||
|
||||
//Fill the bitstream
|
||||
Utils::bitstream bs;
|
||||
for (unsigned int i = 1; i < dataLen; i++) {
|
||||
if (i + 2 < dataLen && (memcmp(data + i, "\000\000\003", 3) == 0)){//Emulation prevention bytes
|
||||
//Yes, we increase i here
|
||||
bs.append(data + i, 2);
|
||||
i += 2;
|
||||
} else {
|
||||
//No we don't increase i here
|
||||
bs.append(data + i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
char profileIdc = bs.get(8);
|
||||
//Start skipping unused data
|
||||
bs.skip(16);
|
||||
bs.getUExpGolomb();
|
||||
if (profileIdc == 100 || profileIdc == 110 || profileIdc == 122 || profileIdc == 244 || profileIdc == 44 || profileIdc == 83 || profileIdc == 86 || profileIdc == 118 || profileIdc == 128) {
|
||||
//chroma format idc
|
||||
if (bs.getUExpGolomb() == 3) {
|
||||
bs.skip(1);
|
||||
}
|
||||
bs.getUExpGolomb();
|
||||
bs.getUExpGolomb();
|
||||
bs.skip(1);
|
||||
if (bs.get(1)) {
|
||||
DEBUG_MSG(DLVL_DEVEL, "Scaling matrix not implemented yet");
|
||||
}
|
||||
}
|
||||
bs.getUExpGolomb();
|
||||
unsigned int pic_order_cnt_type = bs.getUExpGolomb();
|
||||
if (!pic_order_cnt_type) {
|
||||
bs.getUExpGolomb();
|
||||
} else if (pic_order_cnt_type == 1) {
|
||||
DEBUG_MSG(DLVL_DEVEL, "This part of the implementation is incomplete(2), to be continued. If this message is shown, contact developers immediately.");
|
||||
}
|
||||
bs.getUExpGolomb();
|
||||
bs.skip(1);
|
||||
//Stop skipping data and start doing usefull stuff
|
||||
|
||||
|
||||
widthInMbs = bs.getUExpGolomb() + 1;
|
||||
heightInMapUnits = bs.getUExpGolomb() + 1;
|
||||
|
||||
mbsOnlyFlag = bs.get(1);//Gets used in height calculation
|
||||
if (!mbsOnlyFlag) {
|
||||
bs.skip(1);
|
||||
}
|
||||
bs.skip(1);
|
||||
//cropping flag
|
||||
if (bs.get(1)) {
|
||||
cropHorizontal = bs.getUExpGolomb();//leftOffset
|
||||
cropHorizontal += bs.getUExpGolomb();//rightOffset
|
||||
cropVertical = bs.getUExpGolomb();//topOffset
|
||||
cropVertical += bs.getUExpGolomb();//bottomOffset
|
||||
}
|
||||
|
||||
//vuiParameters
|
||||
if (bs.get(1)) {
|
||||
//Skipping all the paramters we dont use
|
||||
if (bs.get(1)) {
|
||||
if (bs.get(8) == 255) {
|
||||
bs.skip(32);
|
||||
}
|
||||
}
|
||||
if (bs.get(1)) {
|
||||
bs.skip(1);
|
||||
}
|
||||
if (bs.get(1)) {
|
||||
bs.skip(4);
|
||||
if (bs.get(1)) {
|
||||
bs.skip(24);
|
||||
}
|
||||
}
|
||||
if (bs.get(1)) {
|
||||
bs.getUExpGolomb();
|
||||
bs.getUExpGolomb();
|
||||
}
|
||||
|
||||
//Decode timing info
|
||||
if (bs.get(1)) {
|
||||
unsigned int unitsInTick = bs.get(32);
|
||||
unsigned int timeScale = bs.get(32);
|
||||
result.fps = (double)timeScale / (2 * unitsInTick);
|
||||
bs.skip(1);
|
||||
}
|
||||
}
|
||||
|
||||
result.width = (widthInMbs * 16) - (cropHorizontal * 2);
|
||||
result.height = ((mbsOnlyFlag ? 1 : 2) * heightInMapUnits * 16) - (cropVertical * 2);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
18
lib/nal.h
18
lib/nal.h
|
@ -1,9 +1,17 @@
|
|||
#pragma once
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <deque>
|
||||
#include "dtsc.h"
|
||||
|
||||
namespace h264 {
|
||||
struct nalData {
|
||||
unsigned char nalType;
|
||||
unsigned long nalSize;
|
||||
};
|
||||
|
||||
std::deque<nalData> analyseH264Packet(const char * data, unsigned long len);
|
||||
std::deque<int> parseNalSizes(DTSC::Packet & pack);
|
||||
|
||||
///Struct containing pre-calculated metadata of an SPS nal unit. Width and height in pixels, fps in Hz
|
||||
|
@ -45,4 +53,14 @@ namespace h264 {
|
|||
PPS(std::string & InputData): NAL(InputData) {};
|
||||
void analyzePPS();
|
||||
};
|
||||
|
||||
|
||||
class sequenceParameterSet {
|
||||
public:
|
||||
sequenceParameterSet(const char * _data, unsigned long _dataLen);
|
||||
SPSMeta getCharacteristics() const;
|
||||
private:
|
||||
const char * data;
|
||||
unsigned long dataLen;
|
||||
};
|
||||
}//ns h264
|
||||
|
|
|
@ -201,6 +201,31 @@ std::string Util::Procs::getOutputOf(char * const * argv) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
|
||||
///This function prepares a deque for getOutputOf and automatically inserts a NULL at the end of the char* const*
|
||||
char* const* Util::Procs::dequeToArgv(std::deque<std::string> & argDeq){
|
||||
char** ret = (char**)malloc((argDeq.size()+1)*sizeof(char*));
|
||||
for (int i = 0; i<argDeq.size(); i++){
|
||||
ret[i] = (char*)argDeq[i].c_str();
|
||||
}
|
||||
ret[argDeq.size()] = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string Util::Procs::getOutputOf(std::deque<std::string> & argDeq){
|
||||
std::string ret;
|
||||
char* const* argv = dequeToArgv(argDeq);//Note: Do not edit deque before executing command
|
||||
ret = getOutputOf(argv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pid_t Util::Procs::StartPiped(std::deque<std::string> & argDeq, int * fdin, int * fdout, int * fderr) {
|
||||
pid_t ret;
|
||||
char* const* argv = dequeToArgv(argDeq);//Note: Do not edit deque before executing command
|
||||
ret = Util::Procs::StartPiped(argv, fdin, fdout, fderr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Starts a new process with given fds if the name is not already active.
|
||||
/// \return 0 if process was not started, process PID otherwise.
|
||||
/// \arg argv Command for this process.
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <string>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
|
||||
/// Contains utility code, not directly related to streaming media
|
||||
namespace Util {
|
||||
|
@ -19,9 +20,12 @@ namespace Util {
|
|||
static void exit_handler();
|
||||
static void runCmd(std::string & cmd);
|
||||
static void setHandler();
|
||||
static char* const* dequeToArgv(std::deque<std::string> & argDeq);
|
||||
public:
|
||||
static std::string getOutputOf(char * const * argv);
|
||||
static std::string getOutputOf(std::deque<std::string> & argDeq);
|
||||
static pid_t StartPiped(char * const * argv, int * fdin, int * fdout, int * fderr);
|
||||
static pid_t StartPiped(std::deque<std::string> & argDeq, int * fdin, int * fdout, int * fderr);
|
||||
static void Stop(pid_t name);
|
||||
static void Murder(pid_t name);
|
||||
static void StopAll();
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "shared_memory.h"
|
||||
#include "stream.h"
|
||||
#include "procs.h"
|
||||
#include "bitfields.h"
|
||||
|
||||
namespace IPC {
|
||||
|
||||
|
@ -1025,5 +1026,43 @@ namespace IPC {
|
|||
}
|
||||
return (myPage.mapped + offsetOnPage + (hasCounter ? 1 : 0));
|
||||
}
|
||||
|
||||
userConnection::userConnection(char * _data) {
|
||||
data = _data;
|
||||
}
|
||||
|
||||
unsigned long userConnection::getTrackId(size_t offset) const {
|
||||
if (offset >= SIMUL_TRACKS){
|
||||
WARN_MSG("Trying to get track id for entry %lu, while there are only %d entries allowed", offset, SIMUL_TRACKS);
|
||||
return 0;
|
||||
}
|
||||
return Bit::btohl(data + (offset * 6));
|
||||
}
|
||||
|
||||
void userConnection::setTrackId(size_t offset, unsigned long trackId) const {
|
||||
if (offset >= SIMUL_TRACKS){
|
||||
WARN_MSG("Trying to set track id for entry %lu, while there are only %d entries allowed", offset, SIMUL_TRACKS);
|
||||
return;
|
||||
}
|
||||
Bit::htobl(data + (offset * 6), trackId);
|
||||
|
||||
}
|
||||
|
||||
unsigned long userConnection::getKeynum(size_t offset) const {
|
||||
if (offset >= SIMUL_TRACKS){
|
||||
WARN_MSG("Trying to get keynum for entry %lu, while there are only %d entries allowed", offset, SIMUL_TRACKS);
|
||||
return 0;
|
||||
}
|
||||
return Bit::btohs(data + (offset * 6) + 4);
|
||||
}
|
||||
|
||||
void userConnection::setKeynum(size_t offset, unsigned long keynum) {
|
||||
if (offset >= SIMUL_TRACKS){
|
||||
WARN_MSG("Trying to set keynum for entry %lu, while there are only %d entries allowed", offset, SIMUL_TRACKS);
|
||||
return;
|
||||
}
|
||||
Bit::htobs(data + (offset * 6) + 4, keynum);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -228,4 +228,15 @@ namespace IPC {
|
|||
///\brief Whether the payload has a counter, if so, it is added in front of the payload
|
||||
bool hasCounter;
|
||||
};
|
||||
|
||||
class userConnection {
|
||||
public:
|
||||
userConnection(char * _data);
|
||||
unsigned long getTrackId(size_t offset) const;
|
||||
void setTrackId(size_t offset, unsigned long trackId) const;
|
||||
unsigned long getKeynum(size_t offset) const;
|
||||
void setKeynum(size_t offset, unsigned long keynum);
|
||||
private:
|
||||
char * data;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -25,6 +25,11 @@ namespace TS {
|
|||
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.
|
||||
|
@ -34,11 +39,11 @@ namespace TS {
|
|||
if (!fread((void *)strBuf, 188, 1, data)) {
|
||||
return false;
|
||||
}
|
||||
pos=188;
|
||||
if (strBuf[0] != 0x47){
|
||||
INFO_MSG("Failed to read a good packet on pos %lld", pos);
|
||||
return false;
|
||||
}
|
||||
pos=188;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -416,7 +421,7 @@ namespace TS {
|
|||
/// \return A character pointer to the internal packet buffer data
|
||||
const char * Packet::checkAndGetBuffer() const{
|
||||
if (pos != 188) {
|
||||
DEBUG_MSG(DLVL_ERROR, "Size invalid (%d) - invalid data from this point on", pos);
|
||||
DEBUG_MSG(DLVL_HIGH, "Size invalid (%d) - invalid data from this point on", pos);
|
||||
}
|
||||
return strBuf;
|
||||
}
|
||||
|
@ -568,6 +573,11 @@ namespace TS {
|
|||
}
|
||||
|
||||
|
||||
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);
|
||||
|
@ -753,6 +763,12 @@ namespace TS {
|
|||
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];
|
||||
|
|
|
@ -22,6 +22,7 @@ namespace TS {
|
|||
public:
|
||||
//Constructors and fillers
|
||||
Packet();
|
||||
Packet(const Packet & rhs);
|
||||
~Packet();
|
||||
bool FromPointer(const char * data);
|
||||
bool FromFile(FILE * data);
|
||||
|
@ -83,6 +84,7 @@ namespace TS {
|
|||
|
||||
class ProgramAssociationTable : public Packet {
|
||||
public:
|
||||
ProgramAssociationTable & operator = (const Packet & rhs);
|
||||
char getOffset() const;
|
||||
char getTableId() const;
|
||||
short getSectionLength() const;
|
||||
|
@ -119,6 +121,7 @@ namespace TS {
|
|||
class ProgramMappingTable : public Packet {
|
||||
public:
|
||||
ProgramMappingTable();
|
||||
ProgramMappingTable & operator = (const Packet & rhs);
|
||||
char getOffset() const;
|
||||
void setOffset(char newVal);
|
||||
char getTableId() const;
|
||||
|
|
324
lib/ts_stream.cpp
Normal file
324
lib/ts_stream.cpp
Normal file
|
@ -0,0 +1,324 @@
|
|||
#include "ts_stream.h"
|
||||
#include "defines.h"
|
||||
#include "h264.h"
|
||||
#include "nal.h"
|
||||
#include "mp4_generic.h"
|
||||
|
||||
namespace TS {
|
||||
void Stream::parse(char * newPack, unsigned long long bytePos) {
|
||||
Packet newPacket;
|
||||
newPacket.FromPointer(newPack);
|
||||
parse(newPacket, bytePos);
|
||||
}
|
||||
|
||||
void Stream::clear(){
|
||||
pesStreams.clear();
|
||||
pesPositions.clear();
|
||||
payloadSize.clear();
|
||||
outPackets.clear();
|
||||
}
|
||||
|
||||
void Stream::parse(Packet & newPack, unsigned long long bytePos) {
|
||||
int tid = newPack.getPID();
|
||||
if (tid == 0){
|
||||
associationTable = newPack;
|
||||
return;
|
||||
}
|
||||
//If we are here, the packet is not a PAT.
|
||||
//First check if it is listed in the PAT as a PMT track.
|
||||
int pmtCount = associationTable.getProgramCount();
|
||||
for (int i = 0; i < pmtCount; i++){
|
||||
if (tid == associationTable.getProgramPID(i)){
|
||||
mappingTable[tid] = newPack;
|
||||
ProgramMappingEntry entry = mappingTable[tid].getEntry(0);
|
||||
while (entry){
|
||||
unsigned long pid = entry.getElementaryPid();
|
||||
pidToCodec[pid] = entry.getStreamType();
|
||||
entry.advance();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
//If it is not a PMT, check the list of all PMTs to see if this is a new PES track.
|
||||
bool inPMT = false;
|
||||
for (std::map<unsigned long, ProgramMappingTable>::iterator it = mappingTable.begin(); it!= mappingTable.end(); it++){
|
||||
ProgramMappingEntry entry = it->second.getEntry(0);
|
||||
while (entry){
|
||||
if (tid == entry.getElementaryPid()){
|
||||
inPMT = true;
|
||||
break;
|
||||
}
|
||||
entry.advance();
|
||||
}
|
||||
if (inPMT){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!inPMT){
|
||||
HIGH_MSG("Encountered a packet on track %d, but the track is not registered in any PMT", tid);
|
||||
return;
|
||||
}
|
||||
pesStreams[tid].push_back(newPack);
|
||||
pesPositions[tid].push_back(bytePos);
|
||||
if (!newPack.getUnitStart() || pesStreams[tid].size() == 1){
|
||||
payloadSize[tid] += newPack.getPayloadLength();
|
||||
}
|
||||
parsePES(tid);
|
||||
}
|
||||
|
||||
bool Stream::hasPacketOnEachTrack() const {
|
||||
if (!pidToCodec.size()){
|
||||
return false;
|
||||
}
|
||||
for (std::map<unsigned long, unsigned long>::const_iterator it = pidToCodec.begin(); it != pidToCodec.end(); it++){
|
||||
if (!outPackets.count(it->first) || !outPackets.at(it->first).size()){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Stream::hasPacket(unsigned long tid) const {
|
||||
if (!pesStreams.count(tid)){
|
||||
return false;
|
||||
}
|
||||
if (outPackets.count(tid) && outPackets.at(tid).size()){
|
||||
return true;
|
||||
}
|
||||
for (int i = 1; i < pesStreams.find(tid)->second.size(); i++) {
|
||||
if (pesStreams.find(tid)->second.at(i).getUnitStart()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned long long decodePTS(const char * data){
|
||||
unsigned long long time;
|
||||
time = ((data[0] >> 1) & 0x07);
|
||||
time <<= 15;
|
||||
time |= ((int)data[1] << 7) | ((data[2] >> 1) & 0x7F);
|
||||
time <<= 15;
|
||||
time |= ((int)data[3] << 7) | ((data[4] >> 1) & 0x7F);
|
||||
time /= 90;
|
||||
return time;
|
||||
}
|
||||
|
||||
void Stream::parsePES(unsigned long tid){
|
||||
std::deque<Packet> & inStream = pesStreams[tid];
|
||||
if (inStream.size() == 1){
|
||||
return;
|
||||
}
|
||||
if (!inStream.back().getUnitStart()){
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned long long bPos = pesPositions[tid].front();
|
||||
//Create a buffer for the current PES, and remove it from the pesStreams buffer.
|
||||
int paySize = payloadSize[tid];
|
||||
char * payload = (char*)malloc(paySize);
|
||||
int offset = 0;
|
||||
while (inStream.size() != 1){
|
||||
memcpy(payload + offset, inStream.front().getPayload(), inStream.front().getPayloadLength());
|
||||
offset += inStream.front().getPayloadLength();
|
||||
inStream.pop_front();
|
||||
pesPositions[tid].pop_front();
|
||||
}
|
||||
|
||||
//Parse the PES header
|
||||
offset = 0;
|
||||
|
||||
while(offset < paySize){
|
||||
const char * pesHeader = payload + offset;
|
||||
|
||||
//Check for large enough buffer
|
||||
if ((paySize - offset) < 9 || (paySize - offset) < 9 + pesHeader[8]){
|
||||
INFO_MSG("Not enough data on track %lu, discarding remainder of data", tid);
|
||||
break;
|
||||
}
|
||||
|
||||
//Check for valid PES lead-in
|
||||
if(pesHeader[0] != 0 || pesHeader[1] != 0x00 || pesHeader[2] != 0x01){
|
||||
INFO_MSG("Invalid PES Lead in on track %lu, discarding it", tid);
|
||||
break;
|
||||
}
|
||||
|
||||
//Read the payload size.
|
||||
//Note: if the payload size is 0, then we assume the pes packet will cover the entire TS Unit.
|
||||
//Note: this is technically only allowed for video pes streams.
|
||||
unsigned long long realPayloadSize = (((int)pesHeader[4] << 8) | pesHeader[5]);
|
||||
if (!realPayloadSize){
|
||||
realPayloadSize = paySize;
|
||||
}
|
||||
if (pidToCodec[tid] == AAC){
|
||||
realPayloadSize -= (3 + pesHeader[8]);
|
||||
}else{
|
||||
realPayloadSize -= (9 + pesHeader[8]);
|
||||
}
|
||||
|
||||
//Read the metadata for this PES Packet
|
||||
///\todo Determine keyframe-ness
|
||||
unsigned int timeStamp = 0;
|
||||
unsigned int timeOffset = 0;
|
||||
unsigned int pesOffset = 9;
|
||||
if ((pesHeader[7] >> 6) & 0x02){//Check for PTS presence
|
||||
timeStamp = decodePTS(pesHeader + pesOffset);
|
||||
pesOffset += 5;
|
||||
if (((pesHeader[7] & 0xC0) >> 6) & 0x01){//Check for DTS presence (yes, only if PTS present)
|
||||
timeOffset = timeStamp;
|
||||
timeStamp = decodePTS(pesHeader + pesOffset);
|
||||
pesOffset += 5;
|
||||
timeOffset -= timeStamp;
|
||||
}
|
||||
}
|
||||
|
||||
if (paySize - offset - pesOffset < realPayloadSize){
|
||||
INFO_MSG("Not enough data left on track %lu.", tid);
|
||||
break;
|
||||
}
|
||||
|
||||
char * pesPayload = payload + offset + pesOffset;
|
||||
|
||||
//Create a new (empty) DTSC Packet at the end of the buffer
|
||||
if (pidToCodec[tid] == AAC){
|
||||
//Parse all the ADTS packets
|
||||
unsigned long offsetInPes = 0;
|
||||
unsigned long samplesRead = 0;
|
||||
while (offsetInPes < realPayloadSize){
|
||||
outPackets[tid].push_back(DTSC::Packet());
|
||||
aac::adts adtsPack(pesPayload + offsetInPes, realPayloadSize - offsetInPes);
|
||||
if (!adtsInfo.count(tid)){
|
||||
adtsInfo[tid] = adtsPack;
|
||||
}
|
||||
outPackets[tid].back().genericFill(timeStamp + ((samplesRead * 1000) / adtsPack.getFrequency()), timeOffset, tid, adtsPack.getPayload(), adtsPack.getPayloadSize(), bPos, 0);
|
||||
samplesRead += adtsPack.getSampleCount();
|
||||
offsetInPes += adtsPack.getHeaderSize() + adtsPack.getPayloadSize();
|
||||
}
|
||||
}
|
||||
if (pidToCodec[tid] == H264){
|
||||
//Convert from annex b
|
||||
char * parsedData = NULL;
|
||||
bool isKeyFrame = false;
|
||||
unsigned long parsedSize = h264::fromAnnexB(pesPayload, realPayloadSize, parsedData);
|
||||
std::deque<h264::nalData> nalInfo = h264::analyseH264Packet(parsedData, parsedSize);
|
||||
int dataOffset = 0;
|
||||
for (std::deque<h264::nalData>::iterator it = nalInfo.begin(); it != nalInfo.end(); it++){
|
||||
switch (it->nalType){
|
||||
case 0x05: {
|
||||
isKeyFrame = true;
|
||||
break;
|
||||
}
|
||||
case 0x07: {
|
||||
spsInfo[tid] = std::string(parsedData + dataOffset + 4, it->nalSize);
|
||||
break;
|
||||
}
|
||||
case 0x08: {
|
||||
ppsInfo[tid] = std::string(parsedData + dataOffset + 4, it->nalSize);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
dataOffset += 4 + it->nalSize;
|
||||
}
|
||||
outPackets[tid].push_back(DTSC::Packet());
|
||||
outPackets[tid].back().genericFill(timeStamp, timeOffset, tid, parsedData, parsedSize, bPos, isKeyFrame);
|
||||
free(parsedData);
|
||||
}
|
||||
//We are done with the realpayload size, reverse calculation so we know the correct offset increase.
|
||||
if (pidToCodec[tid] == AAC){
|
||||
realPayloadSize += (3 + pesHeader[8]);
|
||||
}else{
|
||||
realPayloadSize += (9 + pesHeader[8]);
|
||||
}
|
||||
offset += realPayloadSize;
|
||||
}
|
||||
free(payload);
|
||||
payloadSize[tid] = inStream.front().getPayloadLength();
|
||||
}
|
||||
|
||||
void Stream::getPacket(unsigned long tid, DTSC::Packet & pack) {
|
||||
pack.null();
|
||||
if (!hasPacket(tid)){
|
||||
ERROR_MSG("Trying to obtain a packet on track %lu, but no full packet is available", tid);
|
||||
return;
|
||||
}
|
||||
|
||||
//Handle the situation where we have DTSC Packets buffered
|
||||
if (outPackets[tid].size()){
|
||||
pack = outPackets[tid].front();
|
||||
outPackets[tid].pop_front();
|
||||
if (!outPackets[tid].size()){
|
||||
payloadSize[tid] = 0;
|
||||
for (std::deque<Packet>::iterator it = pesStreams[tid].begin(); it != pesStreams[tid].end(); it++){
|
||||
//Break this loop on the second TS Packet with the UnitStart flag set, not on the first.
|
||||
if (it->getUnitStart() && it != pesStreams[tid].begin()){
|
||||
break;
|
||||
}
|
||||
payloadSize[tid] += it->getPayloadLength();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Stream::getEarliestPacket(DTSC::Packet & pack){
|
||||
pack.null();
|
||||
if (!hasPacketOnEachTrack()){
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned long packTime = 0xFFFFFFFFull;
|
||||
unsigned long packTrack = 0;
|
||||
|
||||
for (std::map<unsigned long, std::deque<DTSC::Packet> >::iterator it = outPackets.begin(); it != outPackets.end(); it++){
|
||||
if (it->second.front().getTime() < packTime){
|
||||
packTrack = it->first;
|
||||
packTime = it->second.front().getTime();
|
||||
}
|
||||
}
|
||||
pack = outPackets[packTrack].front();
|
||||
outPackets[packTrack].pop_front();
|
||||
}
|
||||
|
||||
void Stream::initializeMetadata(DTSC::Meta & meta) {
|
||||
for (std::map<unsigned long, unsigned long>::const_iterator it = pidToCodec.begin(); it != pidToCodec.end(); it++){
|
||||
if (!meta.tracks.count(it->first) && it->second == H264){
|
||||
if (!spsInfo.count(it->first) || !ppsInfo.count(it->first)){
|
||||
continue;
|
||||
}
|
||||
meta.tracks[it->first].type = "video";
|
||||
meta.tracks[it->first].codec = "H264";
|
||||
meta.tracks[it->first].trackID = it->first;
|
||||
std::string tmpBuffer = spsInfo[it->first];
|
||||
h264::sequenceParameterSet sps(spsInfo[it->first].data(), spsInfo[it->first].size());
|
||||
h264::SPSMeta spsChar = sps.getCharacteristics();
|
||||
meta.tracks[it->first].width = spsChar.width;
|
||||
meta.tracks[it->first].height = spsChar.height;
|
||||
meta.tracks[it->first].fpks = spsChar.fps * 1000;
|
||||
MP4::AVCC avccBox;
|
||||
avccBox.setVersion(1);
|
||||
avccBox.setProfile(spsInfo[it->first][1]);
|
||||
avccBox.setCompatibleProfiles(spsInfo[it->first][2]);
|
||||
avccBox.setLevel(spsInfo[it->first][3]);
|
||||
avccBox.setSPSNumber(1);
|
||||
avccBox.setSPS(spsInfo[it->first]);
|
||||
avccBox.setPPSNumber(1);
|
||||
avccBox.setPPS(ppsInfo[it->first]);
|
||||
meta.tracks[it->first].init = std::string(avccBox.payload(), avccBox.payloadSize());
|
||||
INFO_MSG("Initialized metadata for track %lu, with an SPS of %lu bytes, and a PPS of %lu bytes", it->first, spsInfo[it->first].size(), ppsInfo[it->first].size());
|
||||
}
|
||||
if (!meta.tracks.count(it->first) && it->second == AAC){
|
||||
meta.tracks[it->first].type = "audio";
|
||||
meta.tracks[it->first].codec = "AAC";
|
||||
meta.tracks[it->first].trackID = it->first;
|
||||
meta.tracks[it->first].size = 16;
|
||||
meta.tracks[it->first].rate = adtsInfo[it->first].getFrequency();
|
||||
meta.tracks[it->first].channels = adtsInfo[it->first].getChannelCount();
|
||||
char audioInit[2];//5 bits object type, 4 bits frequency index, 4 bits channel index
|
||||
audioInit[0] = ((adtsInfo[it->first].getAACProfile() & 0x1F) << 3) | ((adtsInfo[it->first].getFrequencyIndex() & 0x0E) >> 1);
|
||||
audioInit[1] = ((adtsInfo[it->first].getFrequencyIndex() & 0x01) << 7) | ((adtsInfo[it->first].getChannelConfig() & 0x0F) << 3);
|
||||
meta.tracks[it->first].init = std::string(audioInit, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
37
lib/ts_stream.h
Normal file
37
lib/ts_stream.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
#include "ts_packet.h"
|
||||
#include "adts.h"
|
||||
#include <map>
|
||||
#include <deque>
|
||||
|
||||
namespace TS {
|
||||
enum codecType {
|
||||
H264 = 0x1B,
|
||||
AAC = 0x0F,
|
||||
AC3 = 0x81
|
||||
};
|
||||
|
||||
class Stream{
|
||||
public:
|
||||
void parse(Packet & newPack, unsigned long long bytePos);
|
||||
void parse(char * newPack, unsigned long long bytePos);
|
||||
bool hasPacketOnEachTrack() const;
|
||||
bool hasPacket(unsigned long tid) const;
|
||||
void getPacket(unsigned long tid, DTSC::Packet & pack);
|
||||
void getEarliestPacket(DTSC::Packet & pack);
|
||||
void initializeMetadata(DTSC::Meta & meta);
|
||||
void clear();
|
||||
private:
|
||||
ProgramAssociationTable associationTable;
|
||||
std::map<unsigned long, ProgramMappingTable> mappingTable;
|
||||
std::map<unsigned long, std::deque<Packet> > pesStreams;
|
||||
std::map<unsigned long, std::deque<unsigned long long> > pesPositions;
|
||||
std::map<unsigned long, unsigned long> payloadSize;
|
||||
std::map<unsigned long, std::deque<DTSC::Packet> > outPackets;
|
||||
std::map<unsigned long, unsigned long> pidToCodec;
|
||||
std::map<unsigned long, aac::adts > adtsInfo;
|
||||
std::map<unsigned long, std::string > spsInfo;
|
||||
std::map<unsigned long, std::string > ppsInfo;
|
||||
|
||||
void parsePES(unsigned long tid);
|
||||
};
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue