RTP rewrite using generic RTP::toDTSC class
This commit is contained in:
parent
9bcd365361
commit
7a03d3e96c
4 changed files with 904 additions and 510 deletions
807
lib/rtp.cpp
807
lib/rtp.cpp
File diff suppressed because it is too large
Load diff
141
lib/rtp.h
141
lib/rtp.h
|
@ -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
|
||||||
|
|
||||||
|
|
417
lib/sdp.cpp
417
lib/sdp.cpp
|
@ -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,136 +692,6 @@ 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){
|
||||||
|
@ -803,239 +699,18 @@ namespace SDP{
|
||||||
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
|
||||||
|
|
||||||
|
|
45
lib/sdp.h
45
lib/sdp.h
|
@ -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 ¶m) const;
|
||||||
|
uint64_t getParamInt(const std::string ¶m) 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 ¶m) const;
|
|
||||||
uint64_t getParamInt(const std::string ¶m) 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
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue