Added support for H264 SPS/PPS updates from RTP data in RTSP input

This commit is contained in:
Thulinma 2017-02-17 21:22:48 +01:00
parent faa696c6d1
commit a86cddc70a
2 changed files with 61 additions and 43 deletions

View file

@ -462,14 +462,6 @@ namespace Mist {
} }
} }
///Helper function to determine if a H264 NAL unit is init data or not
static inline bool isH264Init(char * data){
uint8_t nalType = (data[0] & 0x1F);
// 7 = SPS
// 8 = PPS
return (nalType == 7 || nalType == 8);
}
///Helper function to determine if a H264 NAL unit is a keyframe or not ///Helper function to determine if a H264 NAL unit is a keyframe or not
static inline bool isH264Keyframe(char * data, unsigned long len){ static inline bool isH264Keyframe(char * data, unsigned long len){
uint8_t nalType = (data[0] & 0x1F); uint8_t nalType = (data[0] & 0x1F);
@ -546,23 +538,41 @@ namespace Mist {
if (buffer[i] == 0 && buffer[i+1] == 0 && buffer[i+2] == 0 && buffer[i+3] == 1){ 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 //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 Bit::htobl(buffer+lastStart, (i-lastStart-1)-4);//size-prepend
if (!isH264Init(buffer+lastStart+4)){ h264Packet(ts, track, buffer+lastStart, (i-lastStart-1), isH264Keyframe(buffer+lastStart+4, i-lastStart-5));
h264Packet(ts, track, buffer+lastStart, (i-lastStart-1), isH264Keyframe(buffer+lastStart+4, i-lastStart-5));
}
lastStart = i; lastStart = i;
} }
} }
//Last packet (might be first, if no start codes found) //Last packet (might be first, if no start codes found)
Bit::htobl(buffer+lastStart, (len-lastStart)-4);//size-prepend Bit::htobl(buffer+lastStart, (len-lastStart)-4);//size-prepend
if (!isH264Init(buffer+lastStart+4)){ h264Packet(ts, track, buffer+lastStart, (len-lastStart), isH264Keyframe(buffer+lastStart+4, len-lastStart-4));
h264Packet(ts, track, buffer+lastStart, (len-lastStart), isH264Keyframe(buffer+lastStart+4, len-lastStart-4));
}
} }
void OutRTSP::h264Packet(uint64_t ts, const uint64_t track, const char * buffer, const uint32_t len, bool isKey){ void OutRTSP::h264Packet(uint64_t ts, const uint64_t track, const char * buffer, const uint32_t len, bool isKey){
//Ignore zero-length packets (e.g. only contained init data and nothing else) //Ignore zero-length packets (e.g. only contained init data and nothing else)
if (!len){return;} if (!len){return;}
double fps = h264meta[track].fps;
//Header data? Compare to init, set if needed, and throw away
uint8_t nalType = (buffer[4] & 0x1F);
switch (nalType){
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; uint32_t offset = 0;
uint64_t newTs = ts; uint64_t newTs = ts;
if (fps > 1){ if (fps > 1){
@ -632,9 +642,6 @@ namespace Mist {
} }
if ((pl[0] & 0x1F) < 24){ if ((pl[0] & 0x1F) < 24){
DONTEVEN_MSG("H264 single packet, type %u", (unsigned int)(pl[0] & 0x1F)); DONTEVEN_MSG("H264 single packet, type %u", (unsigned int)(pl[0] & 0x1F));
if (isH264Init(pl)){
return;
}
static char * packBuffer = 0; static char * packBuffer = 0;
static unsigned long packBufferSize = 0; static unsigned long packBufferSize = 0;
unsigned long len = pkt.getPayloadSize(); unsigned long len = pkt.getPayloadSize();
@ -687,11 +694,9 @@ namespace Mist {
while (pos + 1 < pkt.getPayloadSize()){ while (pos + 1 < pkt.getPayloadSize()){
unsigned int pLen = Bit::btohs(pl+pos); unsigned int pLen = Bit::btohs(pl+pos);
isKey |= isH264Keyframe(pl+pos+2, pLen); isKey |= isH264Keyframe(pl+pos+2, pLen);
if (!isH264Init(pl+pos+2)){ Bit::htobl(packBuffer+len, pLen);//size-prepend
Bit::htobl(packBuffer+len, pLen);//size-prepend memcpy(packBuffer+len+4, pl+pos+2, pLen);
memcpy(packBuffer+len+4, pl+pos+2, pLen); len += 4+pLen;
len += 4+pLen;
}
pos += 2+pLen; pos += 2+pLen;
} }
h264Packet((pkt.getTimeStamp() - tracks[track].firstTime + 1) / 90, track, packBuffer, len, isKey); h264Packet((pkt.getTimeStamp() - tracks[track].firstTime + 1) / 90, track, packBuffer, len, isKey);
@ -711,7 +716,8 @@ namespace Mist {
if (fuaCurrLen && ((pkt.getPayload()[1] & 0x80) || (tracks[track].rtpSeq != pkt.getSequence()))){ if (fuaCurrLen && ((pkt.getPayload()[1] & 0x80) || (tracks[track].rtpSeq != pkt.getSequence()))){
WARN_MSG("Ending unfinished FU-A"); WARN_MSG("Ending unfinished FU-A");
INSANE_MSG("H264 FU-A packet incompleted: %lu", fuaCurrLen); INSANE_MSG("H264 FU-A packet incompleted: %lu", fuaCurrLen);
if (isH264Init(fuaBuffer+4)){ uint8_t nalType = (fuaBuffer[4] & 0x1F);
if (nalType == 7 || nalType == 8){
//attempt to detect multiple H264 packets, even though specs disallow it //attempt to detect multiple H264 packets, even though specs disallow it
h264MultiParse((pkt.getTimeStamp() - tracks[track].firstTime + 1) / 90, track, fuaBuffer, fuaCurrLen); h264MultiParse((pkt.getTimeStamp() - tracks[track].firstTime + 1) / 90, track, fuaBuffer, fuaCurrLen);
}else{ }else{
@ -752,7 +758,8 @@ namespace Mist {
if (pkt.getPayload()[1] & 0x40){//last packet if (pkt.getPayload()[1] & 0x40){//last packet
INSANE_MSG("H264 FU-A packet type %u completed: %lu", (unsigned int)(fuaBuffer[4] & 0x1F), fuaCurrLen); INSANE_MSG("H264 FU-A packet type %u completed: %lu", (unsigned int)(fuaBuffer[4] & 0x1F), fuaCurrLen);
if (isH264Init(fuaBuffer+4)){ uint8_t nalType = (fuaBuffer[4] & 0x1F);
if (nalType == 7 || nalType == 8){
//attempt to detect multiple H264 packets, even though specs disallow it //attempt to detect multiple H264 packets, even though specs disallow it
h264MultiParse((pkt.getTimeStamp() - tracks[track].firstTime + 1) / 90, track, fuaBuffer, fuaCurrLen); h264MultiParse((pkt.getTimeStamp() - tracks[track].firstTime + 1) / 90, track, fuaBuffer, fuaCurrLen);
}else{ }else{
@ -768,6 +775,28 @@ namespace Mist {
} }
} }
/// Calculates H264 track metadata from sps and pps data stored in tracks[trackNo]
void OutRTSP::updateH264Init(uint64_t trackNo){
DTSC::Track & Trk = myMeta.tracks[trackNo];
RTPTrack & RTrk = tracks[trackNo];
h264::sequenceParameterSet sps(RTrk.spsData.data(), RTrk.spsData.size());
h264::SPSMeta hMeta = sps.getCharacteristics();
MP4::AVCC avccBox;
avccBox.setVersion(1);
avccBox.setProfile(RTrk.spsData[1]);
avccBox.setCompatibleProfiles(RTrk.spsData[2]);
avccBox.setLevel(RTrk.spsData[3]);
avccBox.setSPSNumber(1);
avccBox.setSPS(RTrk.spsData);
avccBox.setPPSNumber(1);
avccBox.setPPS(RTrk.ppsData);
RTrk.fpsMeta = hMeta.fps;
Trk.width = hMeta.width;
Trk.height = hMeta.height;
Trk.fpks = hMeta.fps * 1000;
Trk.init = std::string(avccBox.payload(), avccBox.payloadSize());
}
void OutRTSP::parseSDP(const std::string & sdp){ void OutRTSP::parseSDP(const std::string & sdp){
std::stringstream ss(sdp); std::stringstream ss(sdp);
std::string to; std::string to;
@ -846,23 +875,9 @@ namespace Mist {
//a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z0LAHtkA2D3m//AUABqxAAADAAEAAAMAMg8WLkg=,aMuDyyA=; profile-level-id=42C01E //a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z0LAHtkA2D3m//AUABqxAAADAAEAAAMAMg8WLkg=,aMuDyyA=; profile-level-id=42C01E
std::string sprop = tracks[trackNo].getParamString("sprop-parameter-sets"); std::string sprop = tracks[trackNo].getParamString("sprop-parameter-sets");
size_t comma = sprop.find(','); size_t comma = sprop.find(',');
std::string spsInfo = Encodings::Base64::decode(sprop.substr(0,comma)); tracks[trackNo].spsData = Encodings::Base64::decode(sprop.substr(0,comma));
std::string ppsInfo = Encodings::Base64::decode(sprop.substr(comma+1)); tracks[trackNo].ppsData = Encodings::Base64::decode(sprop.substr(comma+1));
h264::sequenceParameterSet sps(spsInfo.data(), spsInfo.size()); updateH264Init(trackNo);
h264meta[trackNo] = sps.getCharacteristics();
myMeta.tracks[trackNo].width = h264meta[trackNo].width;
myMeta.tracks[trackNo].height = h264meta[trackNo].height;
myMeta.tracks[trackNo].fpks = h264meta[trackNo].fps * 1000;
MP4::AVCC avccBox;
avccBox.setVersion(1);
avccBox.setProfile(spsInfo[1]);
avccBox.setCompatibleProfiles(spsInfo[2]);
avccBox.setLevel(spsInfo[3]);
avccBox.setSPSNumber(1);
avccBox.setSPS(spsInfo);
avccBox.setPPSNumber(1);
avccBox.setPPS(ppsInfo);
myMeta.tracks[trackNo].init = std::string(avccBox.payload(), avccBox.payloadSize());
} }
continue; continue;
} }

View file

@ -24,7 +24,10 @@ namespace Mist {
std::string transportString; std::string transportString;
std::string control; std::string control;
std::string fmtp; std::string fmtp;
std::string spsData;
std::string ppsData;
uint64_t fpsTime; uint64_t fpsTime;
double fpsMeta;
double fps; double fps;
RTPTrack(){ RTPTrack(){
rtcpSent = 0; rtcpSent = 0;
@ -159,7 +162,6 @@ namespace Mist {
void parseSDP(const std::string & sdp); void parseSDP(const std::string & sdp);
long long connectedAt;///< The timestamp the connection was made, as reference point for RTCP packets. long long connectedAt;///< The timestamp the connection was made, as reference point for RTCP packets.
std::map<int, RTPTrack> tracks;///< List of selected tracks with RTSP-specific session data. std::map<int, RTPTrack> tracks;///< List of selected tracks with RTSP-specific session data.
std::map<int, h264::SPSMeta> h264meta;///< Metadata from SPS of H264 tracks, for input handling.
unsigned int pausepoint;///< Position to pause at, when reached unsigned int pausepoint;///< Position to pause at, when reached
HTTP::Parser HTTP_R, HTTP_S; HTTP::Parser HTTP_R, HTTP_S;
std::string source; std::string source;
@ -170,6 +172,7 @@ namespace Mist {
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); void h264MultiParse(uint64_t ts, const uint64_t track, char * buffer, const uint32_t len);
void h264Packet(uint64_t ts, const uint64_t track, const char * buffer, const uint32_t len, bool isKey); void h264Packet(uint64_t ts, const uint64_t track, const char * buffer, const uint32_t len, bool isKey);
void updateH264Init(uint64_t trackNo);
std::string getStatsName(); std::string getStatsName();
}; };
} }