Improved RTSP output, added support for RTSP input
This commit is contained in:
parent
5c8ebdc5ec
commit
3465f2b587
5 changed files with 866 additions and 288 deletions
56
lib/adts.h
56
lib/adts.h
|
@ -1,4 +1,5 @@
|
|||
#include <string>
|
||||
#include "bitstream.h"
|
||||
|
||||
namespace aac {
|
||||
class adts {
|
||||
|
@ -26,4 +27,59 @@ namespace aac {
|
|||
char * data;
|
||||
unsigned long len;
|
||||
};
|
||||
|
||||
class AudSpecConf{
|
||||
public:
|
||||
static inline uint32_t rate(const std::string & conf){
|
||||
Utils::bitstream bs;
|
||||
bs.append(conf.data(), conf.size());
|
||||
if (bs.get(5) == 31){bs.skip(6);}//skip object type
|
||||
switch (bs.get(4)){//frequency index
|
||||
case 0: return 96000;
|
||||
case 1: return 88200;
|
||||
case 2: return 64000;
|
||||
case 3: return 48000;
|
||||
case 4: return 44100;
|
||||
case 5: return 32000;
|
||||
case 6: return 24000;
|
||||
case 7: return 22050;
|
||||
case 8: return 16000;
|
||||
case 9: return 12000;
|
||||
case 10: return 11025;
|
||||
case 11: return 8000;
|
||||
case 12: return 7350;
|
||||
case 15: return bs.get(24);
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
static inline uint16_t channels(const std::string & conf){
|
||||
Utils::bitstream bs;
|
||||
bs.append(conf.data(), conf.size());
|
||||
if (bs.get(5) == 31){bs.skip(6);}//skip object type
|
||||
if (bs.get(4) == 15){bs.skip(24);}//frequency index
|
||||
return bs.get(4);//channel configuration
|
||||
}
|
||||
static inline uint8_t objtype(const std::string & conf){
|
||||
Utils::bitstream bs;
|
||||
bs.append(conf.data(), conf.size());
|
||||
uint8_t ot = bs.get(5);
|
||||
if (ot == 31){return bs.get(6)+32;}
|
||||
return ot;
|
||||
}
|
||||
static inline uint16_t samples(const std::string & conf){
|
||||
Utils::bitstream bs;
|
||||
bs.append(conf.data(), conf.size());
|
||||
if (bs.get(5) == 31){bs.skip(6);}//skip object type
|
||||
if (bs.get(4) == 15){bs.skip(24);}//frequency index
|
||||
bs.skip(4);//channel configuration
|
||||
if (bs.get(1)){
|
||||
return 960;
|
||||
}else{
|
||||
return 1024;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
35
lib/rtp.cpp
35
lib/rtp.cpp
|
@ -6,12 +6,20 @@
|
|||
|
||||
namespace RTP {
|
||||
double Packet::startRTCP = 0;
|
||||
unsigned int MAX_SEND = 4*1024;
|
||||
unsigned int MAX_SEND = 1500-28;
|
||||
|
||||
unsigned int Packet::getHsize() const {
|
||||
return 12 + 4 * getContribCount();
|
||||
}
|
||||
|
||||
unsigned int Packet::getPayloadSize() const {
|
||||
return datalen - getHsize();
|
||||
}
|
||||
|
||||
char * Packet::getPayload() const {
|
||||
return data + getHsize();
|
||||
}
|
||||
|
||||
unsigned int Packet::getVersion() const {
|
||||
return (data[0] >> 6) & 0x3;
|
||||
}
|
||||
|
@ -70,17 +78,17 @@ namespace RTP {
|
|||
|
||||
void Packet::sendH264(void * socket, void callBack(void *, char *, unsigned int, unsigned int), const char * payload, unsigned int payloadlen, unsigned int channel) {
|
||||
/// \todo This function probably belongs in DMS somewhere.
|
||||
if (payloadlen <= MAX_SEND) {
|
||||
if (payloadlen+getHsize() <= MAX_SEND) {
|
||||
data[1] |= 0x80;//setting the RTP marker bit to 1
|
||||
memcpy(data + getHsize(), payload, payloadlen);
|
||||
callBack(socket, data, getHsize() + payloadlen, channel);
|
||||
sentPackets++;
|
||||
sentBytes += payloadlen;
|
||||
sentBytes += payloadlen+getHsize();
|
||||
increaseSequence();
|
||||
} else {
|
||||
data[1] &= 0x7F;//setting the RTP marker bit to 0
|
||||
unsigned int sent = 0;
|
||||
unsigned int sending = MAX_SEND;//packages are of size MAX_SEND, except for the final one
|
||||
unsigned int sending = MAX_SEND-getHsize()-2;//packages are of size MAX_SEND, except for the final one
|
||||
char initByte = (payload[0] & 0xE0) | 0x1C;
|
||||
char serByte = payload[0] & 0x1F; //ser is now 000
|
||||
data[getHsize()] = initByte;
|
||||
|
@ -90,17 +98,17 @@ namespace RTP {
|
|||
} else {
|
||||
serByte &= 0x7F;//set first bit to 0
|
||||
}
|
||||
if (sent + MAX_SEND >= payloadlen) {
|
||||
if (sent + sending >= payloadlen) {
|
||||
//last package
|
||||
serByte |= 0x40;
|
||||
sending = payloadlen - sent;
|
||||
data[1] |= 0x80;//setting the RTP marker bit to 1
|
||||
}
|
||||
data[getHsize() + 1] = serByte;
|
||||
memcpy(data + getHsize() + 2, payload + 1 + sent, sending); //+1 because
|
||||
memcpy(data + getHsize() + 2, payload + 1 + sent, sending);
|
||||
callBack(socket, data, getHsize() + 2 + sending, channel);
|
||||
sentPackets++;
|
||||
sentBytes += sending;
|
||||
sentBytes += sending+getHsize()+2;
|
||||
sent += sending;
|
||||
increaseSequence();
|
||||
}
|
||||
|
@ -128,19 +136,6 @@ namespace RTP {
|
|||
increaseSequence();
|
||||
}
|
||||
|
||||
/// Stores a long long (64 bits) value of val in network order to the pointer p.
|
||||
inline void Packet::htobll(char * p, long long val) {
|
||||
p[0] = (val >> 56) & 0xFF;
|
||||
p[1] = (val >> 48) & 0xFF;
|
||||
p[2] = (val >> 40) & 0xFF;
|
||||
p[3] = (val >> 32) & 0xFF;
|
||||
p[4] = (val >> 24) & 0xFF;
|
||||
p[5] = (val >> 16) & 0xFF;
|
||||
p[6] = (val >> 8) & 0xFF;
|
||||
p[7] = val & 0xFF;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Packet::sendRTCP(long long & connectedAt, void * socket, unsigned int tid , DTSC::Meta & metadata, void callBack(void *, char *, unsigned int, unsigned int)) {
|
||||
void * rtcpData = malloc(32);
|
||||
|
|
|
@ -28,10 +28,11 @@ namespace RTP {
|
|||
unsigned int datalen; ///<Size of rtp packet
|
||||
int sentPackets;
|
||||
int sentBytes;//Because ugly is beautiful
|
||||
inline void htobll(char * p, long long val);
|
||||
public:
|
||||
static double startRTCP;
|
||||
unsigned int getHsize() const;
|
||||
unsigned int getPayloadSize() const;
|
||||
char * getPayload() const;
|
||||
unsigned int getVersion() const;
|
||||
unsigned int getPadding() const;
|
||||
unsigned int getExtension() const;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -4,24 +4,138 @@
|
|||
#include <mist/socket.h>
|
||||
#include <mist/rtp.h>
|
||||
#include <mist/http_parser.h>
|
||||
#include <mist/encode.h>
|
||||
#include <mist/h264.h>
|
||||
|
||||
namespace Mist {
|
||||
///Structure used to keep track of selected tracks.
|
||||
class trackmeta {
|
||||
class RTPTrack {
|
||||
public:
|
||||
trackmeta(){
|
||||
rtcpSent = 0;
|
||||
channel = 0;
|
||||
UDP = false;
|
||||
initSent = false;
|
||||
}
|
||||
Socket::UDPConnection data;
|
||||
Socket::UDPConnection rtcp;
|
||||
RTP::Packet rtpPacket;/// The RTP packet instance used for this track.
|
||||
RTP::Packet pack;
|
||||
long long rtcpSent;
|
||||
uint64_t firstTime;
|
||||
int channel;/// Channel number, used in TCP sending
|
||||
bool UDP;/// True if sending over UDP, false otherwise
|
||||
bool initSent;
|
||||
uint64_t packCount;
|
||||
uint16_t rtpSeq;
|
||||
std::map<uint16_t, RTP::Packet> packBuffer;
|
||||
uint32_t cPort;
|
||||
std::string transportString;
|
||||
std::string control;
|
||||
std::string fmtp;
|
||||
int8_t offset;
|
||||
RTPTrack(){
|
||||
rtcpSent = 0;
|
||||
channel = -1;
|
||||
firstTime = 0;
|
||||
packCount = 0;
|
||||
offset = 0;
|
||||
cPort = 0;
|
||||
rtpSeq = 0;
|
||||
}
|
||||
std::string getParamString(const std::string & param) const{
|
||||
if (!fmtp.size()){return "";}
|
||||
size_t pos = fmtp.find(param);
|
||||
if (pos == std::string::npos){return "";}
|
||||
pos += param.size()+1;
|
||||
size_t ePos = fmtp.find_first_of(" ;", pos);
|
||||
return fmtp.substr(pos, ePos-pos);
|
||||
}
|
||||
uint64_t getParamInt(const std::string & param) const{
|
||||
return atoll(getParamString(param).c_str());
|
||||
}
|
||||
std::string mediaDescription(const DTSC::Track & trk){
|
||||
std::stringstream mediaDesc;
|
||||
if (trk.codec == "H264") {
|
||||
MP4::AVCC avccbox;
|
||||
avccbox.setPayload(trk.init);
|
||||
mediaDesc << "m=video 0 RTP/AVP 97\r\n"
|
||||
"a=rtpmap:97 H264/90000\r\n"
|
||||
"a=cliprect:0,0," << trk.height << "," << trk.width << "\r\n"
|
||||
"a=framesize:97 " << trk.width << '-' << trk.height << "\r\n"
|
||||
"a=fmtp:97 packetization-mode=1;profile-level-id="
|
||||
<< std::hex << std::setw(2) << std::setfill('0') << (int)trk.init.data()[1] << std::dec << "E0"
|
||||
<< std::hex << std::setw(2) << std::setfill('0') << (int)trk.init.data()[3] << std::dec << ";"
|
||||
"sprop-parameter-sets="
|
||||
<< Encodings::Base64::encode(std::string(avccbox.getSPS(), avccbox.getSPSLen()))
|
||||
<< ","
|
||||
<< Encodings::Base64::encode(std::string(avccbox.getPPS(), avccbox.getPPSLen()))
|
||||
<< "\r\n"
|
||||
"a=framerate:" << ((double)trk.fpks)/1000.0 << "\r\n"
|
||||
"a=control:track" << trk.trackID << "\r\n";
|
||||
} else if (trk.codec == "AAC") {
|
||||
mediaDesc << "m=audio 0 RTP/AVP 96" << "\r\n"
|
||||
"a=rtpmap:96 mpeg4-generic/" << trk.rate << "/" << trk.channels << "\r\n"
|
||||
"a=fmtp:96 streamtype=5; profile-level-id=15; config=";
|
||||
for (unsigned int i = 0; i < trk.init.size(); i++) {
|
||||
mediaDesc << std::hex << std::setw(2) << std::setfill('0') << (int)trk.init[i] << std::dec;
|
||||
}
|
||||
//these values are described in RFC 3640
|
||||
mediaDesc << "; mode=AAC-hbr; SizeLength=13; IndexLength=3; IndexDeltaLength=3;\r\n"
|
||||
"a=control:track" << trk.trackID << "\r\n";
|
||||
}else if (trk.codec == "MP3") {
|
||||
mediaDesc << "m=" << trk.type << " 0 RTP/AVP 14" << "\r\n"
|
||||
"a=rtpmap:14 MPA/90000/" << trk.channels << "\r\n"
|
||||
"a=control:track" << trk.trackID << "\r\n";
|
||||
}else if ( trk.codec == "AC3") {
|
||||
mediaDesc << "m=audio 0 RTP/AVP 100" << "\r\n"
|
||||
"a=rtpmap:100 AC3/" << trk.rate << "/" << trk.channels << "\r\n"
|
||||
"a=control:track" << trk.trackID << "\r\n";
|
||||
}
|
||||
return mediaDesc.str();
|
||||
}
|
||||
bool parseTransport(const std::string & transport, const std::string & host, const std::string & source, const DTSC::Track & trk){
|
||||
unsigned int SSrc = rand();
|
||||
if (trk.codec == "H264") {
|
||||
pack = RTP::Packet(97, 1, 0, SSrc);
|
||||
}else if(trk.codec == "AAC"){
|
||||
pack = RTP::Packet(96, 1, 0, SSrc);
|
||||
}else if(trk.codec == "AC3"){
|
||||
pack = RTP::Packet(100, 1, 0, SSrc);
|
||||
}else if(trk.codec == "MP3"){
|
||||
pack = RTP::Packet(14, 1, 0, SSrc);
|
||||
}else{
|
||||
ERROR_MSG("Unsupported codec %s for RTSP on track %u", trk.codec.c_str(), trk.trackID);
|
||||
return false;
|
||||
}
|
||||
std::cerr << transport << std::endl;
|
||||
if (transport.find("TCP") != std::string::npos) {
|
||||
std::string chanE = transport.substr(transport.find("interleaved=") + 12, (transport.size() - transport.rfind('-') - 1)); //extract channel ID
|
||||
channel = atol(chanE.c_str());
|
||||
rtcpSent = 0;
|
||||
transportString = transport;
|
||||
} else {
|
||||
channel = -1;
|
||||
size_t port_loc = transport.rfind("client_port=") + 12;
|
||||
cPort = atol(transport.substr(port_loc, transport.rfind('-') - port_loc).c_str());
|
||||
unsigned int rand_offset = ((rand() % 4000) << 1) + 2000;
|
||||
//find available ports locally;
|
||||
int sendbuff = 4*1024*1024;
|
||||
data.SetDestination(host, cPort);
|
||||
data.bind(rand_offset + trk.trackID * 2);
|
||||
setsockopt(data.getSock(), SOL_SOCKET, SO_SNDBUF, &sendbuff, sizeof(sendbuff));
|
||||
rtcp.SetDestination(host, cPort + 1);
|
||||
rtcp.bind(rand_offset + trk.trackID * 2 + 1);
|
||||
setsockopt(rtcp.getSock(), SOL_SOCKET, SO_SNDBUF, &sendbuff, sizeof(sendbuff));
|
||||
std::stringstream tStr;
|
||||
tStr << "RTP/AVP/UDP;unicast;client_port=" << cPort << '-' << cPort + 1 << ";source="<< source <<";server_port=" << (rand_offset + trk.trackID * 2) << "-" << (rand_offset + trk.trackID * 2 + 1) << ";ssrc=" << std::hex << SSrc << std::dec;
|
||||
transportString = tStr.str();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
std::string rtpInfo(const DTSC::Track & trk, const std::string & source, uint64_t currentTime){
|
||||
unsigned int timeMultiplier = 1;
|
||||
if (trk.codec == "H264") {
|
||||
timeMultiplier = 90;
|
||||
} else if (trk.codec == "AAC" || trk.codec == "MP3" || trk.codec == "AC3") {
|
||||
timeMultiplier = ((double)trk.rate / 1000.0);
|
||||
}
|
||||
std::stringstream rInfo;
|
||||
rInfo << "url=" << source << "/track" << trk.trackID << ";"; //get the current url, not localhost
|
||||
rInfo << "sequence=" << pack.getSequence() << ";rtptime=" << currentTime * timeMultiplier;
|
||||
return rInfo.str();
|
||||
}
|
||||
};
|
||||
|
||||
class OutRTSP : public Output {
|
||||
|
@ -30,17 +144,23 @@ namespace Mist {
|
|||
static void init(Util::Config * cfg);
|
||||
void sendNext();
|
||||
void onRequest();
|
||||
void requestHandler();
|
||||
bool isReadyForPlay();
|
||||
bool onFinish();
|
||||
private:
|
||||
void handleDescribe();
|
||||
void handleSetup();
|
||||
void handlePlay();
|
||||
void handlePause();
|
||||
|
||||
bool isPushing;
|
||||
void parseSDP(const std::string & sdp);
|
||||
long long connectedAt;///< The timestamp the connection was made, as reference point for RTCP packets.
|
||||
std::map<int, trackmeta> tracks;///< List of selected tracks with RTSP-specific session data.
|
||||
unsigned int seekpoint;///< Current play position
|
||||
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
|
||||
HTTP::Parser HTTP_R, HTTP_S;
|
||||
std::string source;
|
||||
bool expectTCP;
|
||||
bool handleTCP();
|
||||
void handleUDP();
|
||||
void handleIncomingRTP(const uint64_t track, const RTP::Packet & pkt);
|
||||
void h264Packet(uint64_t ts, const uint64_t track, const char * buffer, const uint32_t len, const bool isKey);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue