RTP rewrite using generic RTP::toDTSC class

This commit is contained in:
Thulinma 2018-06-08 22:32:26 +02:00
parent 9bcd365361
commit 7a03d3e96c
4 changed files with 904 additions and 510 deletions

File diff suppressed because it is too large Load diff

143
lib/rtp.h
View file

@ -1,9 +1,12 @@
#pragma once #pragma once
#include "dtsc.h" #include "dtsc.h"
#include "h264.h"
#include "h265.h"
#include "json.h" #include "json.h"
#include "mp4.h" #include "mp4.h"
#include "mp4_generic.h" #include "mp4_generic.h"
#include "socket.h" #include "socket.h"
#include "util.h"
#include <algorithm> #include <algorithm>
#include <cstdio> #include <cstdio>
#include <deque> #include <deque>
@ -30,9 +33,8 @@ namespace RTP{
class Packet{ class Packet{
private: private:
bool managed; bool managed;
char *data; ///<The actual RTP packet that is being sent char *data; ///< The actual RTP packet that is being sent
uint32_t maxDataLen; ///< Amount of reserved bytes for the packet(s) uint32_t maxDataLen; ///< Amount of reserved bytes for the packet(s)
unsigned int datalen; ///<Size of rtp packet
int sentPackets; int sentPackets;
int sentBytes; // Because ugly is beautiful int sentBytes; // Because ugly is beautiful
public: public:
@ -47,26 +49,29 @@ namespace RTP{
unsigned int getMarker() const; unsigned int getMarker() const;
unsigned int getPayloadType() const; unsigned int getPayloadType() const;
unsigned int getSequence() const; unsigned int getSequence() const;
unsigned int getTimeStamp() const; uint32_t getTimeStamp() const;
void setSequence(unsigned int seq); void setSequence(unsigned int seq);
unsigned int getSSRC() const; unsigned int getSSRC() const;
void setSSRC(unsigned long ssrc); void setSSRC(unsigned long ssrc);
void setTimestamp(unsigned int t); void setTimestamp(uint32_t t);
void increaseSequence(); void increaseSequence();
void sendH264(void *socket, void callBack(void *, char *, unsigned int, unsigned int), void sendH264(void *socket, void callBack(void *, char *, unsigned int, unsigned int),
const char *payload, unsigned int payloadlen, unsigned int channel); const char *payload, unsigned int payloadlen, unsigned int channel, bool lastOfAccesUnit);
void sendVP8(void *socket, void callBack(void *, char *, unsigned int, unsigned int),
const char *payload, unsigned int payloadlen, unsigned int channel);
void sendH265(void *socket, void callBack(void *, char *, unsigned int, unsigned int), void sendH265(void *socket, void callBack(void *, char *, unsigned int, unsigned int),
const char *payload, unsigned int payloadlen, unsigned int channel); const char *payload, unsigned int payloadlen, unsigned int channel);
void sendMPEG2(void *socket, void callBack(void *, char *, unsigned int, unsigned int), void sendMPEG2(void *socket, void callBack(void *, char *, unsigned int, unsigned int),
const char *payload, unsigned int payloadlen, unsigned int channel); const char *payload, unsigned int payloadlen, unsigned int channel);
void sendData(void *socket, void callBack(void *, char *, unsigned int, unsigned int), void sendData(void *socket, void callBack(void *, char *, unsigned int, unsigned int),
const char *payload, unsigned int payloadlen, unsigned int channel, const char *payload, unsigned int payloadlen, unsigned int channel,
std::string codec); std::string codec);
void sendRTCP_SR(long long &connectedAt, void *socket, unsigned int tid, DTSC::Meta &metadata, void sendRTCP_SR(long long &connectedAt, void *socket, unsigned int tid, DTSC::Meta &metadata,
void callBack(void *, char *, unsigned int, unsigned int)); void callBack(void *, char *, unsigned int, unsigned int));
void sendRTCP_RR(long long &connectedAt, SDP::Track & sTrk, unsigned int tid, DTSC::Meta &metadata, void sendRTCP_RR(long long &connectedAt, SDP::Track &sTrk, unsigned int tid,
void callBack(void *, char *, unsigned int, unsigned int)); DTSC::Meta &metadata,
void callBack(void *, char *, unsigned int, unsigned int));
Packet(); Packet();
Packet(unsigned int pt, unsigned int seq, unsigned int ts, unsigned int ssr, Packet(unsigned int pt, unsigned int seq, unsigned int ts, unsigned int ssr,
@ -76,37 +81,105 @@ namespace RTP{
~Packet(); ~Packet();
Packet(const char *dat, unsigned int len); Packet(const char *dat, unsigned int len);
char *getData(); char *getData();
char *ptr() const { return data; }
}; };
/// Sorts RTP packets, outputting them through a callback in correct order.
/// Also keeps track of statistics, which it expects to be read/reset externally (for now).
/// Optionally can be inherited from with the outPacket function overridden to not use a callback.
class Sorter{ class Sorter{
public: public:
Sorter(); Sorter(uint64_t trackId = 0, void (*callback)(const uint64_t track, const Packet &p) = 0);
void addPacket(const char *dat, unsigned int len); bool wantSeq(uint16_t seq) const;
void addPacket(const Packet & pack); void addPacket(const char *dat, unsigned int len);
void setCallback(uint64_t track, void (*callback)(const uint64_t track, const Packet &p)); void addPacket(const Packet &pack);
uint16_t rtpSeq; // By default, calls the callback function, if set.
int32_t lostTotal, lostCurrent; virtual void outPacket(const uint64_t track, const Packet &p){
uint32_t packTotal, packCurrent; if (callback){callback(track, p);}
private: }
uint64_t packTrack; void setCallback(uint64_t track, void (*callback)(const uint64_t track, const Packet &p));
std::map<uint16_t, Packet> packBuffer; uint16_t rtpSeq;
void (*callback)(const uint64_t track, const Packet &p); int32_t lostTotal, lostCurrent;
uint32_t packTotal, packCurrent;
private:
uint64_t packTrack;
std::map<uint16_t, Packet> packBuffer;
std::map<uint16_t, Packet> packetHistory;
void (*callback)(const uint64_t track, const Packet &p);
}; };
class MPEGVideoHeader{ class MPEGVideoHeader{
public: public:
MPEGVideoHeader(char * d); MPEGVideoHeader(char *d);
void clear(); void clear();
uint16_t getTotalLen() const; uint16_t getTotalLen() const;
std::string toString() const; std::string toString() const;
void setTempRef(uint16_t ref); void setTempRef(uint16_t ref);
void setPictureType(uint8_t pType); void setPictureType(uint8_t pType);
void setSequence(); void setSequence();
void setBegin(); void setBegin();
void setEnd(); void setEnd();
private:
char * data; private:
char *data;
}; };
} /// Converts (sorted) RTP packets into DTSC packets.
/// Outputs DTSC packets through a callback function or overridden virtual function.
/// Updates init data through a callback function or overridden virtual function.
class toDTSC{
public:
toDTSC();
void setProperties(const uint64_t track, const std::string &codec, const std::string &type,
const std::string &init, const double multiplier);
void setProperties(const DTSC::Track &Trk);
void setCallbacks(void (*cbPack)(const DTSC::Packet &pkt),
void (*cbInit)(const uint64_t track, const std::string &initData));
void addRTP(const RTP::Packet &rPkt);
virtual void outPacket(const DTSC::Packet &pkt){
if (cbPack){cbPack(pkt);}
}
virtual void outInit(const uint64_t track, const std::string &initData){
if (cbInit){cbInit(track, initData);}
}
public:
uint64_t trackId;
double multiplier; ///< Multiplier to convert from millis to RTP time
std::string codec; ///< Codec of this track
std::string type; ///< Type of this track
std::string init; ///< Init data of this track
unsigned int lastSeq; ///< Last sequence number seen
uint64_t packCount; ///< Amount of DTSC packets outputted, for H264/HEVC
double fps; ///< Framerate, for H264, HEVC
uint32_t wrapArounds; ///< Counter for RTP timestamp wrapArounds
bool recentWrap; ///< True if a wraparound happened recently.
uint32_t prevTime;
uint64_t firstTime;
void (*cbPack)(const DTSC::Packet &pkt);
void (*cbInit)(const uint64_t track, const std::string &initData);
// Codec-specific handlers
void handleAAC(uint64_t msTime, char *pl, uint32_t plSize);
void handleMP2(uint64_t msTime, char *pl, uint32_t plSize);
void handleMPEG2(uint64_t msTime, char *pl, uint32_t plSize);
void handleHEVC(uint64_t msTime, char *pl, uint32_t plSize, bool missed);
void handleHEVCSingle(uint64_t ts, const char *buffer, const uint32_t len, bool isKey);
h265::initData hevcInfo; ///< For HEVC init parsing
Util::ResizeablePointer fuaBuffer; ///< For H264/HEVC FU-A packets
Util::ResizeablePointer packBuffer; ///< For H264/HEVC regular and STAP packets
void handleH264(uint64_t msTime, char *pl, uint32_t plSize, bool missed, bool hasPadding);
void handleH264Single(uint64_t ts, const char *buffer, const uint32_t len, bool isKey);
void handleH264Multi(uint64_t ts, char *buffer, const uint32_t len);
std::string spsData; ///< SPS for H264
std::string ppsData; ///< PPS for H264
void handleVP8(uint64_t msTime, const char *buffer, const uint32_t len, bool missed,
bool hasPadding);
Util::ResizeablePointer
vp8FrameBuffer; ///< Stores successive VP8 payload data. We always start with the first
///< partition; but we might be missing other partitions when they were
///< lost. (a partition is basically what's called a slice in H264).
bool vp8BufferHasKeyframe;
};
}// namespace RTP

View file

@ -8,6 +8,13 @@
#include "util.h" #include "util.h"
namespace SDP{ namespace SDP{
static State *snglState = 0;
static void snglStateInitCallback(const uint64_t track, const std::string &initData){
snglState->updateInit(track, initData);
}
Track::Track(){ Track::Track(){
rtcpSent = 0; rtcpSent = 0;
channel = -1; channel = -1;
@ -54,15 +61,23 @@ namespace SDP{
"a=fmtp:97 packetization-mode=1;profile-level-id=" "a=fmtp:97 packetization-mode=1;profile-level-id="
<< std::hex << std::setw(2) << std::setfill('0') << (int)trk.init.data()[1] << std::hex << std::setw(2) << std::setfill('0') << (int)trk.init.data()[1]
<< std::dec << "E0" << std::hex << std::setw(2) << std::setfill('0') << std::dec << "E0" << std::hex << std::setw(2) << std::setfill('0')
<< (int)trk.init.data()[3] << std::dec << (int)trk.init.data()[3] << std::dec << ";"
<< ";" << "sprop-parameter-sets=";
"sprop-parameter-sets=" size_t count = avccbox.getSPSCount();
<< Encodings::Base64::encode(std::string(avccbox.getSPS(), avccbox.getSPSLen())) for (size_t i = 0; i < count; ++i){
<< "," mediaDesc << (i ? "," : "")
<< Encodings::Base64::encode(std::string(avccbox.getPPS(), avccbox.getPPSLen())) << Encodings::Base64::encode(
<< "\r\n" std::string(avccbox.getSPS(i), avccbox.getSPSLen(i)));
"a=framerate:" }
<< ((double)trk.fpks) / 1000.0 mediaDesc << ",";
count = avccbox.getPPSCount();
for (size_t i = 0; i < count; ++i){
mediaDesc << (i ? "," : "")
<< Encodings::Base64::encode(
std::string(avccbox.getPPS(i), avccbox.getPPSLen(i)));
}
mediaDesc << "\r\n"
<< "a=framerate:" << ((double)trk.fpks) / 1000.0
<< "\r\n" << "\r\n"
"a=control:track" "a=control:track"
<< trk.trackID << "\r\n"; << trk.trackID << "\r\n";
@ -205,9 +220,9 @@ namespace SDP{
rtcp.SetDestination(dest, 1337); rtcp.SetDestination(dest, 1337);
portA = portB = 0; portA = portB = 0;
int retries = 0; int retries = 0;
while (portB != portA+1 && retries < 10){ while (portB != portA + 1 && retries < 10){
portA = data.bind(0); portA = data.bind(0);
portB = rtcp.bind(portA+1); portB = rtcp.bind(portA + 1);
} }
std::stringstream tStr; std::stringstream tStr;
tStr << "RTP/AVP/UDP;unicast;client_port=" << portA << "-" << portB; tStr << "RTP/AVP/UDP;unicast;client_port=" << portA << "-" << portB;
@ -335,6 +350,12 @@ namespace SDP{
return rInfo.str(); return rInfo.str();
} }
State::State(){
incomingPacketCallback = 0;
myMeta = 0;
snglState = this;
}
void State::parseSDP(const std::string &sdp){ void State::parseSDP(const std::string &sdp){
DONTEVEN_MSG("Parsing %llu-byte SDP", sdp.size()); DONTEVEN_MSG("Parsing %llu-byte SDP", sdp.size());
std::stringstream ss(sdp); std::stringstream ss(sdp);
@ -430,9 +451,11 @@ namespace SDP{
} }
} }
} }
tConv[trackNo].setProperties(*thisTrack);
HIGH_MSG("Incoming track %s", thisTrack->getIdentifier().c_str()); HIGH_MSG("Incoming track %s", thisTrack->getIdentifier().c_str());
continue; continue;
} }
if (nope){continue;}// ignore lines if we have no valid track if (nope){continue;}// ignore lines if we have no valid track
// RTP mapping // RTP mapping
if (to.substr(0, 8) == "a=rtpmap"){ if (to.substr(0, 8) == "a=rtpmap"){
@ -487,6 +510,7 @@ namespace SDP{
if (!thisTrack->codec.size()){ if (!thisTrack->codec.size()){
ERROR_MSG("Unsupported RTP mapping: %s", mediaType.c_str()); ERROR_MSG("Unsupported RTP mapping: %s", mediaType.c_str());
}else{ }else{
tConv[trackNo].setProperties(*thisTrack);
HIGH_MSG("Incoming track %s", thisTrack->getIdentifier().c_str()); HIGH_MSG("Incoming track %s", thisTrack->getIdentifier().c_str());
} }
continue; continue;
@ -576,6 +600,7 @@ namespace SDP{
Trk.width = MI.width; Trk.width = MI.width;
Trk.height = MI.height; Trk.height = MI.height;
Trk.fpks = RTrk.fpsMeta * 1000; Trk.fpks = RTrk.fpsMeta * 1000;
tConv[trackNo].setProperties(Trk);
} }
/// Calculates H264 track metadata from vps, sps and pps data stored in tracks[trackNo] /// Calculates H264 track metadata from vps, sps and pps data stored in tracks[trackNo]
@ -598,6 +623,7 @@ namespace SDP{
Trk.height = hMeta.height; Trk.height = hMeta.height;
Trk.fpks = hMeta.fps * 1000; Trk.fpks = hMeta.fps * 1000;
Trk.init = std::string(avccBox.payload(), avccBox.payloadSize()); Trk.init = std::string(avccBox.payload(), avccBox.payloadSize());
tConv[trackNo].setProperties(Trk);
} }
uint32_t State::getTrackNoForChannel(uint8_t chan){ uint32_t State::getTrackNoForChannel(uint8_t chan){
@ -666,376 +692,25 @@ namespace SDP{
return 0; return 0;
} }
/// Handles a single H264 packet, checking if others are appended at the end in Annex B format.
/// If so, splits them up and calls h264Packet for each. If not, calls it only once for the whole
/// payload.
void State::h264MultiParse(uint64_t ts, const uint64_t track, char *buffer, const uint32_t len){
uint32_t lastStart = 0;
for (uint32_t i = 0; i < len - 4; ++i){
// search for start code
if (buffer[i] == 0 && buffer[i + 1] == 0 && buffer[i + 2] == 0 && buffer[i + 3] == 1){
// if found, handle a packet from the last start code up to this start code
Bit::htobl(buffer + lastStart, (i - lastStart - 1) - 4); // size-prepend
h264Packet(ts, track, buffer + lastStart, (i - lastStart - 1),
h264::isKeyframe(buffer + lastStart + 4, i - lastStart - 5));
lastStart = i;
}
}
// Last packet (might be first, if no start codes found)
Bit::htobl(buffer + lastStart, (len - lastStart) - 4); // size-prepend
h264Packet(ts, track, buffer + lastStart, (len - lastStart),
h264::isKeyframe(buffer + lastStart + 4, len - lastStart - 4));
}
void State::h264Packet(uint64_t ts, const uint64_t track, const char *buffer, const uint32_t len,
bool isKey){
MEDIUM_MSG("H264: %llu@%llu, %lub%s", track, ts, len, isKey ? " (key)" : "");
// Ignore zero-length packets (e.g. only contained init data and nothing else)
if (!len){return;}
// Header data? Compare to init, set if needed, and throw away
uint8_t nalType = (buffer[4] & 0x1F);
if (nalType == 9 && len < 20){return;}// ignore delimiter-only packets
switch (nalType){
case 6: //SEI
return;
case 7: // SPS
if (tracks[track].spsData.size() != len - 4 ||
memcmp(buffer + 4, tracks[track].spsData.data(), len - 4) != 0){
INFO_MSG("Updated SPS from RTP data");
tracks[track].spsData.assign(buffer + 4, len - 4);
updateH264Init(track);
}
return;
case 8: // PPS
if (tracks[track].ppsData.size() != len - 4 ||
memcmp(buffer + 4, tracks[track].ppsData.data(), len - 4) != 0){
INFO_MSG("Updated PPS from RTP data");
tracks[track].ppsData.assign(buffer + 4, len - 4);
updateH264Init(track);
}
return;
default: // others, continue parsing
break;
}
double fps = tracks[track].fpsMeta;
uint32_t offset = 0;
uint64_t newTs = ts;
if (fps > 1){
// Assume a steady frame rate, clip the timestamp based on frame number.
uint64_t frameNo = (ts / (1000.0 / fps)) + 0.5;
while (frameNo < tracks[track].packCount){tracks[track].packCount--;}
// More than 32 frames behind? We probably skipped something, somewhere...
if ((frameNo - tracks[track].packCount) > 32){tracks[track].packCount = frameNo;}
// After some experimentation, we found that the time offset is the difference between the
// frame number and the packet counter, times the frame rate in ms
offset = (frameNo - tracks[track].packCount) * (1000.0 / fps);
//... and the timestamp is the packet counter times the frame rate in ms.
newTs = tracks[track].packCount * (1000.0 / fps);
VERYHIGH_MSG("Packing time %llu = %sframe %llu (%.2f FPS). Expected %llu -> +%llu/%lu", ts,
isKey ? "key" : "i", frameNo, fps, tracks[track].packCount,
(frameNo - tracks[track].packCount), offset);
}else{
// For non-steady frame rate, assume no offsets are used and the timestamp is already correct
VERYHIGH_MSG("Packing time %llu = %sframe %llu (variable rate)", ts, isKey ? "key" : "i",
tracks[track].packCount);
}
// Fill the new DTSC packet, buffer it.
DTSC::Packet nextPack;
nextPack.genericFill(newTs, offset, track, buffer, len, 0, isKey);
tracks[track].packCount++;
if (incomingPacketCallback){incomingPacketCallback(nextPack);}
}
void State::h265Packet(uint64_t ts, const uint64_t track, const char *buffer, const uint32_t len,
bool isKey){
MEDIUM_MSG("H265: %llu@%llu, %lub%s", track, ts, len, isKey ? " (key)" : "");
// Ignore zero-length packets (e.g. only contained init data and nothing else)
if (!len){return;}
// Header data? Compare to init, set if needed, and throw away
uint8_t nalType = (buffer[4] & 0x7E) >> 1;
switch (nalType){
case 32: // VPS
case 33: // SPS
case 34: // PPS
tracks[track].hevcInfo.addUnit(buffer);
updateH265Init(track);
return;
default: // others, continue parsing
break;
}
double fps = tracks[track].fpsMeta;
uint32_t offset = 0;
uint64_t newTs = ts;
if (fps > 1){
// Assume a steady frame rate, clip the timestamp based on frame number.
uint64_t frameNo = (ts / (1000.0 / fps)) + 0.5;
while (frameNo < tracks[track].packCount){tracks[track].packCount--;}
// More than 32 frames behind? We probably skipped something, somewhere...
if ((frameNo - tracks[track].packCount) > 32){tracks[track].packCount = frameNo;}
// After some experimentation, we found that the time offset is the difference between the
// frame number and the packet counter, times the frame rate in ms
offset = (frameNo - tracks[track].packCount) * (1000.0 / fps);
//... and the timestamp is the packet counter times the frame rate in ms.
newTs = tracks[track].packCount * (1000.0 / fps);
VERYHIGH_MSG("Packing time %llu = %sframe %llu (%.2f FPS). Expected %llu -> +%llu/%lu", ts,
isKey ? "key" : "i", frameNo, fps, tracks[track].packCount,
(frameNo - tracks[track].packCount), offset);
}else{
// For non-steady frame rate, assume no offsets are used and the timestamp is already correct
VERYHIGH_MSG("Packing time %llu = %sframe %llu (variable rate)", ts, isKey ? "key" : "i",
tracks[track].packCount);
}
// Fill the new DTSC packet, buffer it.
DTSC::Packet nextPack;
nextPack.genericFill(newTs, offset, track, buffer, len, 0, isKey);
tracks[track].packCount++;
if (incomingPacketCallback){incomingPacketCallback(nextPack);}
}
/// Returns the multiplier to use to get milliseconds from the RTP payload type for the given /// Returns the multiplier to use to get milliseconds from the RTP payload type for the given
/// track /// track
double getMultiplier(const DTSC::Track &Trk){ double getMultiplier(const DTSC::Track &Trk){
if (Trk.type == "video" || Trk.codec == "MP2" || Trk.codec == "MP3"){return 90.0;} if (Trk.type == "video" || Trk.codec == "MP2" || Trk.codec == "MP3"){return 90.0;}
return ((double)Trk.rate / 1000.0); return ((double)Trk.rate / 1000.0);
} }
void State::updateInit(const uint64_t trackNo, const std::string &initData){
if (myMeta->tracks.count(trackNo)){
myMeta->tracks[trackNo].init = initData;
}
}
/// Handles RTP packets generically, for both TCP and UDP-based connections. /// Handles RTP packets generically, for both TCP and UDP-based connections.
/// In case of UDP, expects packets to be pre-sorted. /// In case of UDP, expects packets to be pre-sorted.
void State::handleIncomingRTP(const uint64_t track, const RTP::Packet &pkt){ void State::handleIncomingRTP(const uint64_t track, const RTP::Packet &pkt){
DTSC::Track &Trk = myMeta->tracks[track]; tConv[track].setCallbacks(incomingPacketCallback, snglStateInitCallback);
if (!tracks[track].firstTime){tracks[track].firstTime = pkt.getTimeStamp() + 1;} tConv[track].addRTP(pkt);
uint64_t millis = (pkt.getTimeStamp() - tracks[track].firstTime + 1) / getMultiplier(Trk);
char *pl = pkt.getPayload();
uint32_t plSize = pkt.getPayloadSize();
INSANE_MSG("Received RTP packet for track %llu, time %llu -> %llu", track, pkt.getTimeStamp(),
millis);
if (Trk.codec == "ALAW" || Trk.codec == "opus" || Trk.codec == "PCM" || Trk.codec == "ULAW"){
DTSC::Packet nextPack;
nextPack.genericFill(millis, 0, track, pl, plSize, 0, false);
if (incomingPacketCallback){incomingPacketCallback(nextPack);}
return;
}
if (Trk.codec == "AAC"){
// assume AAC packets are single AU units
/// \todo Support other input than single AU units
unsigned int headLen =
(Bit::btohs(pl) >> 3) + 2; // in bits, so /8, plus two for the prepended size
DTSC::Packet nextPack;
uint16_t samples = aac::AudSpecConf::samples(Trk.init);
uint32_t sampleOffset = 0;
uint32_t offset = 0;
uint32_t auSize = 0;
for (uint32_t i = 2; i < headLen; i += 2){
auSize = Bit::btohs(pl + i) >> 3; // only the upper 13 bits
nextPack.genericFill(
(pkt.getTimeStamp() + sampleOffset - tracks[track].firstTime + 1) / getMultiplier(Trk),
0, track, pl + headLen + offset, std::min(auSize, plSize - headLen - offset), 0, false);
offset += auSize;
sampleOffset += samples;
if (incomingPacketCallback){incomingPacketCallback(nextPack);}
}
return;
}
if (Trk.codec == "MP2" || Trk.codec == "MP3"){
if (plSize < 5){
WARN_MSG("Empty packet ignored!");
return;
}
DTSC::Packet nextPack;
nextPack.genericFill(millis, 0, track, pl + 4, plSize - 4, 0, false);
if (incomingPacketCallback){incomingPacketCallback(nextPack);}
return;
}
if (Trk.codec == "MPEG2"){
if (plSize < 5){
WARN_MSG("Empty packet ignored!");
return;
}
///\TODO Merge packets with same timestamp together
HIGH_MSG("Received MPEG2 packet: %s", RTP::MPEGVideoHeader(pl).toString().c_str());
DTSC::Packet nextPack;
nextPack.genericFill(millis, 0, track, pl + 4, plSize - 4, 0, false);
if (incomingPacketCallback){incomingPacketCallback(nextPack);}
return;
}
if (Trk.codec == "HEVC"){
if (plSize < 2){
WARN_MSG("Empty packet ignored!");
return;
}
uint8_t nalType = (pl[0] & 0x7E) >> 1;
if (nalType == 48){
ERROR_MSG("AP not supported yet");
}else if (nalType == 49){
DONTEVEN_MSG("H265 Fragmentation Unit");
static Util::ResizeablePointer fuaBuffer;
// No length yet? Check for start bit. Ignore rest.
if (!fuaBuffer.size() && (pl[2] & 0x80) == 0){
HIGH_MSG("Not start of a new FU - throwing away");
return;
}
if (fuaBuffer.size() && ((pl[2] & 0x80) || (tracks[track].sorter.rtpSeq != pkt.getSequence()))){
WARN_MSG("H265 FU packet incompleted: %lu", fuaBuffer.size());
Bit::htobl(fuaBuffer, fuaBuffer.size() - 4); // size-prepend
fuaBuffer[4] |= 0x80; // set error bit
h265Packet((pkt.getTimeStamp() - tracks[track].firstTime + 1) / 90, track, fuaBuffer,
fuaBuffer.size(), h265::isKeyframe(fuaBuffer + 4, fuaBuffer.size() - 4));
fuaBuffer.size() = 0;
return;
}
unsigned long len = plSize - 3; // ignore the three FU bytes in front
if (!fuaBuffer.size()){len += 6;}// six extra bytes for the first packet
if (!fuaBuffer.allocate(fuaBuffer.size() + len)){return;}
if (!fuaBuffer.size()){
memcpy(fuaBuffer + 6, pl + 3, plSize - 3);
// reconstruct first byte
fuaBuffer[4] = ((pl[2] & 0x3F) << 1) | (pl[0] & 0x81);
fuaBuffer[5] = pl[1];
}else{
memcpy(fuaBuffer + fuaBuffer.size(), pl + 3, plSize - 3);
}
fuaBuffer.size() += len;
if (pl[2] & 0x40){// last packet
VERYHIGH_MSG("H265 FU packet type %s (%u) completed: %lu",
h265::typeToStr((fuaBuffer[4] & 0x7E) >> 1),
(uint8_t)((fuaBuffer[4] & 0x7E) >> 1), fuaBuffer.size());
Bit::htobl(fuaBuffer, fuaBuffer.size() - 4); // size-prepend
h265Packet((pkt.getTimeStamp() - tracks[track].firstTime + 1) / 90, track, fuaBuffer,
fuaBuffer.size(), h265::isKeyframe(fuaBuffer + 4, fuaBuffer.size() - 4));
fuaBuffer.size() = 0;
}
return;
}else if (nalType == 50){
ERROR_MSG("PACI/TSCI not supported yet");
}else{
DONTEVEN_MSG("%s NAL unit (%u)", h265::typeToStr(nalType), nalType);
static Util::ResizeablePointer packBuffer;
if (!packBuffer.allocate(plSize + 4)){return;}
Bit::htobl(packBuffer, plSize); // size-prepend
memcpy(packBuffer + 4, pl, plSize);
h265Packet((pkt.getTimeStamp() - tracks[track].firstTime + 1) / 90, track, packBuffer,
plSize + 4, h265::isKeyframe(packBuffer + 4, plSize));
return;
}
return;
}
if (Trk.codec == "H264"){
// Handles common H264 packets types, but not all.
// Generalizes and converts them all to a data format ready for DTSC, then calls h264Packet
// for that data.
// Prints a WARN-level message if packet type is unsupported.
/// \todo Support other H264 packets types?
if (!plSize){
WARN_MSG("Empty packet ignored!");
return;
}
if ((pl[0] & 0x1F) == 0){
WARN_MSG("H264 packet type null ignored");
return;
}
if ((pl[0] & 0x1F) < 24){
DONTEVEN_MSG("H264 single packet, type %u", (unsigned int)(pl[0] & 0x1F));
static Util::ResizeablePointer packBuffer;
if (!packBuffer.allocate(plSize + 4)){return;}
Bit::htobl(packBuffer, plSize); // size-prepend
memcpy(packBuffer + 4, pl, plSize);
h264Packet((pkt.getTimeStamp() - tracks[track].firstTime + 1) / 90, track, packBuffer,
plSize + 4, h264::isKeyframe(packBuffer + 4, plSize));
return;
}
if ((pl[0] & 0x1F) == 24){
DONTEVEN_MSG("H264 STAP-A packet");
unsigned int len = 0;
unsigned int pos = 1;
while (pos + 1 < plSize){
unsigned int pLen = Bit::btohs(pl + pos);
INSANE_MSG("Packet of %ub and type %u", pLen, (unsigned int)(pl[pos + 2] & 0x1F));
pos += 2 + pLen;
len += 4 + pLen;
}
static Util::ResizeablePointer packBuffer;
if (!packBuffer.allocate(len)){return;}
pos = 1;
len = 0;
bool isKey = false;
while (pos + 1 < plSize){
unsigned int pLen = Bit::btohs(pl + pos);
isKey |= h264::isKeyframe(pl + pos + 2, pLen);
Bit::htobl(packBuffer + len, pLen); // size-prepend
memcpy(packBuffer + len + 4, pl + pos + 2, pLen);
len += 4 + pLen;
pos += 2 + pLen;
}
h264Packet((pkt.getTimeStamp() - tracks[track].firstTime + 1) / 90, track, packBuffer, len,
isKey);
return;
}
if ((pl[0] & 0x1F) == 28){
DONTEVEN_MSG("H264 FU-A packet");
static Util::ResizeablePointer fuaBuffer;
// No length yet? Check for start bit. Ignore rest.
if (!fuaBuffer.size() && (pl[1] & 0x80) == 0){
HIGH_MSG("Not start of a new FU-A - throwing away");
return;
}
if (fuaBuffer.size() && ((pl[1] & 0x80) || (tracks[track].sorter.rtpSeq != pkt.getSequence()))){
WARN_MSG("Ending unfinished FU-A");
INSANE_MSG("H264 FU-A packet incompleted: %lu", fuaBuffer.size());
uint8_t nalType = (fuaBuffer[4] & 0x1F);
if (nalType == 7 || nalType == 8){
// attempt to detect multiple H264 packets, even though specs disallow it
h264MultiParse((pkt.getTimeStamp() - tracks[track].firstTime + 1) / 90, track,
fuaBuffer, fuaBuffer.size());
}else{
Bit::htobl(fuaBuffer, fuaBuffer.size() - 4); // size-prepend
fuaBuffer[4] |= 0x80; // set error bit
h264Packet((pkt.getTimeStamp() - tracks[track].firstTime + 1) / 90, track, fuaBuffer,
fuaBuffer.size(), h264::isKeyframe(fuaBuffer + 4, fuaBuffer.size() - 4));
}
fuaBuffer.size() = 0;
return;
}
unsigned long len = plSize - 2; // ignore the two FU-A bytes in front
if (!fuaBuffer.size()){len += 5;}// five extra bytes for the first packet
if (!fuaBuffer.allocate(fuaBuffer.size() + len)){return;}
if (!fuaBuffer.size()){
memcpy(fuaBuffer + 4, pl + 1, plSize - 1);
// reconstruct first byte
fuaBuffer[4] = (fuaBuffer[4] & 0x1F) | (pl[0] & 0xE0);
}else{
memcpy(fuaBuffer + fuaBuffer.size(), pl + 2, plSize - 2);
}
fuaBuffer.size() += len;
if (pl[1] & 0x40){// last packet
INSANE_MSG("H264 FU-A packet type %u completed: %lu", (unsigned int)(fuaBuffer[4] & 0x1F),
fuaBuffer.size());
uint8_t nalType = (fuaBuffer[4] & 0x1F);
if (nalType == 7 || nalType == 8){
// attempt to detect multiple H264 packets, even though specs disallow it
h264MultiParse((pkt.getTimeStamp() - tracks[track].firstTime + 1) / 90, track,
fuaBuffer, fuaBuffer.size());
}else{
Bit::htobl(fuaBuffer, fuaBuffer.size() - 4); // size-prepend
h264Packet((pkt.getTimeStamp() - tracks[track].firstTime + 1) / 90, track, fuaBuffer,
fuaBuffer.size(), h264::isKeyframe(fuaBuffer + 4, fuaBuffer.size() - 4));
}
fuaBuffer.size() = 0;
}
return;
}
WARN_MSG("H264 packet type %u unsupported", (unsigned int)(pl[0] & 0x1F));
return;
}
} }
}// namespace SDP }// namespace SDP

View file

@ -1,15 +1,26 @@
#include "dtsc.h" #include "dtsc.h"
#include "h265.h"
#include "http_parser.h" #include "http_parser.h"
#include "rtp.h" #include "rtp.h"
#include "socket.h" #include "socket.h"
#include "h265.h" #include <vector>
namespace SDP{ namespace SDP{
double getMultiplier(const DTSC::Track & Trk); double getMultiplier(const DTSC::Track &Trk);
/// Structure used to keep track of selected tracks. /// Structure used to keep track of selected tracks.
class Track{ class Track{
public:
Track();
std::string generateTransport(uint32_t trackNo, const std::string &dest = "",
bool TCPmode = true);
std::string getParamString(const std::string &param) const;
uint64_t getParamInt(const std::string &param) const;
bool parseTransport(const std::string &transport, const std::string &host,
const std::string &source, const DTSC::Track &trk);
std::string rtpInfo(const DTSC::Track &trk, const std::string &source, uint64_t currentTime);
public: public:
Socket::UDPConnection data; Socket::UDPConnection data;
Socket::UDPConnection rtcp; Socket::UDPConnection rtcp;
@ -29,38 +40,26 @@ namespace SDP{
uint64_t fpsTime; uint64_t fpsTime;
double fpsMeta; double fpsMeta;
double fps; double fps;
Track();
std::string generateTransport(uint32_t trackNo, const std::string &dest = "", bool TCPmode = true);
std::string getParamString(const std::string &param) const;
uint64_t getParamInt(const std::string &param) const;
bool parseTransport(const std::string &transport, const std::string &host,
const std::string &source, const DTSC::Track &trk);
std::string rtpInfo(const DTSC::Track &trk, const std::string &source, uint64_t currentTime);
}; };
class State{ class State{
public: public:
State(){ State();
incomingPacketCallback = 0;
myMeta = 0;
}
DTSC::Meta *myMeta;
void (*incomingPacketCallback)(const DTSC::Packet &pkt); void (*incomingPacketCallback)(const DTSC::Packet &pkt);
std::map<uint32_t, Track> tracks; ///< List of selected tracks with SDP-specific session data.
void parseSDP(const std::string &sdp); void parseSDP(const std::string &sdp);
void parseSDPEx(const std::string &sdp);
void updateH264Init(uint64_t trackNo); void updateH264Init(uint64_t trackNo);
void updateH265Init(uint64_t trackNo); void updateH265Init(uint64_t trackNo);
void updateInit(const uint64_t trackNo, const std::string &initData);
uint32_t getTrackNoForChannel(uint8_t chan); uint32_t getTrackNoForChannel(uint8_t chan);
uint32_t parseSetup(HTTP::Parser &H, const std::string &host, uint32_t parseSetup(HTTP::Parser &H, const std::string &host, const std::string &source);
const std::string &source);
void handleIncomingRTP(const uint64_t track, const RTP::Packet &pkt); void handleIncomingRTP(const uint64_t track, const RTP::Packet &pkt);
void h264MultiParse(uint64_t ts, const uint64_t track, char *buffer, const uint32_t len); public:
void h264Packet(uint64_t ts, const uint64_t track, const char *buffer, const uint32_t len, DTSC::Meta *myMeta;
bool isKey); std::map<uint32_t, RTP::toDTSC> tConv; ///<Converters to DTSC
void h265Packet(uint64_t ts, const uint64_t track, const char *buffer, const uint32_t len, std::map<uint32_t, Track> tracks; ///< List of selected tracks with SDP-specific session data.
bool isKey);
}; };
std::string mediaDescription(const DTSC::Track &trk); std::string mediaDescription(const DTSC::Track &trk);
} }// namespace SDP