Added SDP input

This commit is contained in:
Marco 2021-01-22 18:08:44 +01:00 committed by Thulinma
parent dd2382e858
commit 3e73508a6a
8 changed files with 571 additions and 46 deletions

View file

@ -506,6 +506,7 @@ namespace RTP{
rtpSeq = pSNo - 5;
first = false;
}
DONTEVEN_MSG("Received packet #%u, current packet is #%u", pSNo, rtpSeq);
if (preBuffer){
//If we've buffered the first 5 packets, assume we have the first one known
if (packBuffer.size() >= 5){
@ -583,6 +584,7 @@ namespace RTP{
packCount = 0;
lastSeq = 0;
vp8BufferHasKeyframe = false;
curPicParameterSetId = 0;
}
void toDTSC::setProperties(const uint64_t track, const std::string &c, const std::string &t,
@ -601,7 +603,7 @@ namespace RTP{
MP4::AVCC avccbox;
avccbox.setPayload(init);
spsData.assign(avccbox.getSPS(), avccbox.getSPSLen());
ppsData.assign(avccbox.getPPS(), avccbox.getPPSLen());
ppsData[curPicParameterSetId].assign(avccbox.getPPS(), avccbox.getPPSLen());
h264::sequenceParameterSet sps(spsData.data(), spsData.size());
h264::SPSMeta hMeta = sps.getCharacteristics();
fps = hMeta.fps;
@ -669,6 +671,12 @@ namespace RTP{
}
}
}
// When there are B-frames, the firstTime can be higher than the current time
// causing msTime to become negative and thus overflow
if (firstTime > pTime + 1){
WARN_MSG("firstTime was higher than current packet time. Readjusting firsTime...");
firstTime = pTime + 1;
}
prevTime = pkt.getTimeStamp();
uint64_t msTime = ((uint64_t)pTime - firstTime + 1 + 0x100000000ull * wrapArounds) / multiplier + milliSync;
char *pl = (char *)pkt.getPayload();
@ -844,9 +852,13 @@ namespace RTP{
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 < packCount){packCount--;}
if (frameNo < packCount){
packCount = frameNo;
}
// More than 32 frames behind? We probably skipped something, somewhere...
if ((frameNo - packCount) > 32){packCount = frameNo;}
if ((frameNo - packCount) > 32){
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 - packCount) * (1000.0 / fps);
@ -981,9 +993,13 @@ namespace RTP{
if (fps > 1){
// Assume a steady frame rate, clip the timestamp based on frame number.
uint64_t frameNo = (currH264Time / (1000.0 / fps)) + 0.5;
while (frameNo < packCount){packCount--;}
if (frameNo < packCount){
packCount = frameNo;
}
// More than 32 frames behind? We probably skipped something, somewhere...
if ((frameNo - packCount) > 32){packCount = frameNo;}
if ((frameNo - packCount) > 32){
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 - packCount) * (1000.0 / fps);
@ -1026,58 +1042,50 @@ namespace RTP{
spsData.assign(buffer + 4, len - 4);
h264::SPSMeta hMeta = sps.getCharacteristics();
fps = hMeta.fps;
MP4::AVCC avccBox;
avccBox.setVersion(1);
avccBox.setProfile(spsData[1]);
avccBox.setCompatibleProfiles(spsData[2]);
avccBox.setLevel(spsData[3]);
avccBox.setSPSCount(1);
avccBox.setSPS(spsData);
avccBox.setPPSCount(1);
avccBox.setPPS(ppsData);
std::string newInit = std::string(avccBox.payload(), avccBox.payloadSize());
if (newInit != init){
init = newInit;
outInit(trackId, init);
}
}
return;
case 8: // PPS
if (ppsData.size() != len - 4 || memcmp(buffer + 4, ppsData.data(), len - 4) != 0){
if (!h264::ppsValidate(buffer+4, len-4)){
WARN_MSG("Ignoring invalid PPS packet! (%" PRIu32 "b)", len-4);
return;
}
HIGH_MSG("Updated PPS from RTP data: %" PRIu32 "b", len-4);
ppsData.assign(buffer + 4, len - 4);
MP4::AVCC avccBox;
avccBox.setVersion(1);
avccBox.setProfile(spsData[1]);
avccBox.setCompatibleProfiles(spsData[2]);
avccBox.setLevel(spsData[3]);
avccBox.setSPSCount(1);
avccBox.setSPS(spsData);
avccBox.setPPSCount(1);
avccBox.setPPS(ppsData);
std::string newInit = std::string(avccBox.payload(), avccBox.payloadSize());
if (newInit != init){
init = newInit;
outInit(trackId, init);
// Determine pic_parameter_set_id and check whether the PPS is new or updated
{
h264::ppsUnit PPS(buffer + 4, len - 4);
if (ppsData[PPS.picParameterSetId].size() != len - 4 || memcmp(buffer + 4, ppsData[PPS.picParameterSetId].data(), len - 4) != 0){
if (!h264::ppsValidate(buffer+4, len-4)){
WARN_MSG("Ignoring invalid PPS packet! (%" PRIu32 "b)", len-4);
return;
}
HIGH_MSG("Updated PPS with ID %li from RTP data", PPS.picParameterSetId);
ppsData[PPS.picParameterSetId].assign(buffer + 4, len - 4);
}
}
return;
case 5:{
//If this is a keyframe and we have no buffer yet, prepend the SPS/PPS
if (!h264OutBuffer.size()){
// We have a keyframe: prepend SPS/PPS if the pic_parameter_set_id changed or if this is the first keyframe
h264::codedSliceUnit keyPiece(buffer + 4, len - 4);
if (!h264OutBuffer.size() || keyPiece.picParameterSetId != curPicParameterSetId){
curPicParameterSetId = keyPiece.picParameterSetId;
// Update meta init data if needed
MP4::AVCC avccBox;
avccBox.setVersion(1);
avccBox.setProfile(spsData[1]);
avccBox.setCompatibleProfiles(spsData[2]);
avccBox.setLevel(spsData[3]);
avccBox.setSPSCount(1);
avccBox.setSPS(spsData);
avccBox.setPPSCount(1);
avccBox.setPPS(ppsData[curPicParameterSetId]);
std::string newInit = std::string(avccBox.payload(), avccBox.payloadSize());
if (newInit != init){
init = newInit;
outInit(trackId, init);
}
// Prepend SPS/PPS
char sizeBuffer[4];
Bit::htobl(sizeBuffer, spsData.size());
h264OutBuffer.append(sizeBuffer, 4);
h264OutBuffer.append(spsData.data(), spsData.size());
Bit::htobl(sizeBuffer, ppsData.size());
Bit::htobl(sizeBuffer, ppsData[curPicParameterSetId].size());
h264OutBuffer.append(sizeBuffer, 4);
h264OutBuffer.append(ppsData.data(), ppsData.size());
h264OutBuffer.append(ppsData[curPicParameterSetId].data(), ppsData[curPicParameterSetId].size());
}
//Note: no return, we still want to buffer the packet itself, below!
}

View file

@ -179,7 +179,8 @@ namespace RTP{
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
uint8_t curPicParameterSetId;
std::map<uint8_t,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

View file

@ -398,6 +398,60 @@ namespace SDP{
return true;
}
/// Tries to bind a RTP/RTCP UDP port pair
/// \param portInfo port/#ports as found in SDP file
/// \param hostInfo host address
bool Track::bindUDPPort(std::string portInfo, std::string hostInfo){
uint32_t portRTP, portRTCP;
if (portInfo == "" || hostInfo == ""){
WARN_MSG("Can not setup transport to address %s:%s", hostInfo.c_str(), portInfo.c_str());
return false;
}
// Extract port numbers from input string
size_t tempPos;
tempPos = portInfo.find('/');
if (tempPos != std::string::npos){
// TODO https://tools.ietf.org/html/rfc4566#section-5.14
// bind more ports if theres a /, which indicates the amount of port pairs
WARN_MSG("Does not support more than one RTP/RTCP port pair");
portInfo = portInfo.substr(0, tempPos);
}
std::istringstream ( portInfo ) >> portRTP;
portRTCP = portRTP + 1;
// During RTSP streams we get the transport info on setup
// in this case the port is set to 0 in the SDP file
if (!portRTP){
return true;
}
// Since default is set to IPV6, force to AF_UNSPEC
data.setSocketFamily(AF_UNSPEC);
rtcp.setSocketFamily(AF_UNSPEC);
// Test UDP ports
int sendbuff = 4 * 1024 * 1024;
data.SetDestination(hostInfo, portRTP);
setsockopt(data.getSock(), SOL_SOCKET, SO_SNDBUF, &sendbuff, sizeof(sendbuff));
rtcp.SetDestination(hostInfo, portRTCP);
setsockopt(rtcp.getSock(), SOL_SOCKET, SO_SNDBUF, &sendbuff, sizeof(sendbuff));
// Bind sockets
portA = data.bind(portRTP, hostInfo);
if (portA != portRTP){
FAIL_MSG("Server requested RTP port %u, which we couldn't bind", portRTP);
return false;
}
portB = rtcp.bind(portRTCP, hostInfo);
if (portB != portRTCP){
FAIL_MSG("Server requested RTCP port %u, which we couldn't bind", portRTCP);
return false;
}
return true;
}
/// Gets the rtpInfo for a given DTSC::Track, source identifier and timestamp (in millis).
std::string Track::rtpInfo(const DTSC::Meta &M, size_t tid, const std::string &source, uint64_t currentTime){
std::stringstream rInfo;
@ -414,8 +468,14 @@ namespace SDP{
void State::parseSDP(const std::string &sdp){
DONTEVEN_MSG("Parsing %zu-byte SDP", sdp.size());
if (!sdp.size()){
FAIL_MSG("SDP buffer is empty!");
return;
}
std::stringstream ss(sdp);
std::string to;
// (UDP) Host will be set when a c= line is read
std::string host = "127.0.0.1";
size_t tid = INVALID_TRACK_ID;
bool nope = true; // true if we have no valid track to fill
while (std::getline(ss, to, '\n')){
@ -423,12 +483,39 @@ namespace SDP{
if (to.empty()){continue;}
DONTEVEN_MSG("Parsing SDP line: %s", to.c_str());
// Extract host IP from c= line
// c=<nettype> <addrtype> <connection-address>
if (to.substr(0, 2) == "c="){
// Strip c=
std::stringstream words(to.substr(2));
std::string item;
size_t tempPos;
// Strip nettype
getline(words, item, ' ');
// Strip addrtype
getline(words, item, ' ');
// Get connection address
getline(words, item, ' ');
// Strip TTL, which is appended as IP/TTL
tempPos = item.find('/');
if (tempPos != std::string::npos){
item = item.substr(0, tempPos);
}
host = item;
}
// All tracks start with a media line
// m=<media> <port>/<number of ports> <proto> <fmt> ...
if (to.substr(0, 2) == "m="){
nope = true;
tid = myMeta->addTrack();
// Strip m=
std::stringstream words(to.substr(2));
std::string item;
// Get media type
if (getline(words, item, ' ') && (item == "audio" || item == "video")){
myMeta->setType(tid, item);
myMeta->setID(tid, tid);
@ -438,13 +525,22 @@ namespace SDP{
tracks.erase(tid);
continue;
}
// Get port info and bind RTP/RTCP UDP pairs
getline(words, item, ' ');
if (!tracks[tid].bindUDPPort(item, host) ){
FAIL_MSG("Failed to bind ports for given port info: %s", item.c_str());
}
// Get transport protocol
if (!getline(words, item, ' ') || item.substr(0, 7) != "RTP/AVP"){
WARN_MSG("Media transport not supported: %s", item.c_str());
myMeta->removeTrack(tid);
tracks.erase(tid);
continue;
}
// Get media format description
if (getline(words, item, ' ')){
uint64_t avp_type = JSON::Value(item).asInt();
switch (avp_type){
@ -774,4 +870,22 @@ namespace SDP{
tConv[track].addRTP(pkt);
}
/// Re-inits internal variables and removes all tracks from meta
void State::reinitSDP(){
tConv.clear();
size_t trackID;
for (std::map<long unsigned int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) {
trackID = myMeta->getID(it->first);
INFO_MSG("Removing track %zu:%s", it->first, myMeta->getTrackIdentifier(it->first).c_str());
if (trackID == INVALID_TRACK_ID){
WARN_MSG("TrackID was invalid");
}
else{
myMeta->removeTrack(it->first);
}
}
//myMeta->refresh();
tracks.clear();
}
}// namespace SDP

View file

@ -14,6 +14,8 @@ namespace SDP{
public:
Track();
std::string generateTransport(uint32_t trackNo, const std::string &dest = "", bool TCPmode = true);
/// Tries to bind a RTP/RTCP UDP port pair
bool bindUDPPort(std::string portInfo, std::string hostInfo);
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,
@ -55,6 +57,10 @@ namespace SDP{
size_t getTrackNoForChannel(uint8_t chan);
size_t parseSetup(HTTP::Parser &H, const std::string &host, const std::string &source);
void handleIncomingRTP(const uint64_t track, const RTP::Packet &pkt);
// Sets up the transport from SDP data
bool parseTransport(const std::string &sdpString);
// Re-inits internal variables and removes all tracks from meta
void reinitSDP();
public:
DTSC::Meta *myMeta;

View file

@ -1701,6 +1701,7 @@ void Socket::UDPConnection::setSocketFamily(int AF_TYPE){\
/// Stores the properties of the receiving end of this UDP socket.
/// This will be the receiving end for all SendNow calls.
void Socket::UDPConnection::SetDestination(std::string destIp, uint32_t port){
DONTEVEN_MSG("Setting destination to %s:%u", destIp.c_str(), port);
// UDP sockets can switch between IPv4 and IPv6 on demand.
// We change IPv4-mapped IPv6 addresses into IPv4 addresses for Windows-sillyness reasons.
if (destIp.substr(0, 7) == "::ffff:"){destIp = destIp.substr(7);}