Added support for H264 SPS/PPS updates from RTP data in RTSP input
This commit is contained in:
parent
faa696c6d1
commit
a86cddc70a
2 changed files with 61 additions and 43 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue