两个协议标准定义了RTP H264的传输规范
RFC3550
RFC6184
c++实例RFC3550我自己常用JRTPLIB
使用ffmpeg测试验证
用ffmpeg 向本机发送rtp流
Output #0, rtp, to 'rtp://127.0.0.1:30016':
Metadata:
encoder : Lavf58.45.100
Stream #0:0: Video: h264 (libx264), nv12, 1920x1080, q=-1--1, 30 fps, 90k tbn, 30 tbc
Metadata:
encoder : Lavc58.91.100 libx264
Side data:
cpb: bitrate max/min/avg: 20000000/0/0 buffer size: 1000000 vbv_delay: N/A
SDP:
v=0
o=- 0 0 IN IP4 127.0.0.1
s=No Name
c=IN IP4 127.0.0.1
t=0 0
a=tool:libavformat 58.45.100
m=video 30016 RTP/AVP 96
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1
frame= 39 fps= 30 q=18.0 Lsize= 578kB time=00:00:01.26 bitrate=3737.0kbits/s dup=12 drop=0 speed=0.982x
video:572kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 1.025276%
使用JRTPLIB自带的tests 测试用例rtcpdump验证
#include "rtpsession.h"
#include "rtpipv4address.h"
#include "rtpsessionparams.h"
#include "rtpudpv4transmitter.h"
#include "rtperrors.h"
#include "rtcpcompoundpacket.h"
#include "rtcpsrpacket.h"
#include "rtcprrpacket.h"
#include "rtcpbyepacket.h"
#include "rtprawpacket.h"
#include <stdio.h>
#include <iostream>
using namespace jrtplib;
void checkError(int status)
{
if (status >= 0)
return;
std::cerr << "An error occured in the RTP component: " << std::endl;
std::cerr << "Error description: " << RTPGetErrorString(status) << std::endl;
exit(-1);
}
class MyRTPSession : public RTPSession
{
private:
FILE *pLogFile;
public:
MyRTPSession()
{
SetChangeIncomingData(true);
pLogFile = fopen("logfile.dat", "wb");
}
~MyRTPSession()
{
if (pLogFile)
fclose(pLogFile);
}
protected:
void OnValidatedRTPPacket(RTPSourceData *srcdat, RTPPacket *rtppack, bool isonprobation, bool *ispackethandled)
{
// Make sure no RTP packets are stored internally, we'd just be wasting memory
DeletePacket(rtppack);
*ispackethandled = true;
}
bool OnChangeIncomingData(RTPRawPacket *pPack)
{
if (pLogFile)
{
double t = pPack->GetReceiveTime().GetDouble();
bool isRTP = pPack->IsRTP();
uint32_t dataLength = (uint32_t)pPack->GetDataLength();
if (isRTP)
fwrite("RTP ", 1, 4, pLogFile);
else
fwrite("RTCP", 1, 4, pLogFile);
fwrite(&t, 1, sizeof(double), pLogFile);
fwrite(&dataLength, 1, sizeof(uint32_t),pLogFile);
fwrite(pPack->GetData(), 1, dataLength, pLogFile);
}
return true;
}
void OnRTCPCompoundPacket(RTCPCompoundPacket *p, const RTPTime &receivetime, const RTPAddress *senderaddress)
{
printf("%u.%06u RECEIVED\n",receivetime.GetSeconds(),receivetime.GetMicroSeconds());
DumpCompoundPacket(stdout,p);
}
void OnSendRTCPCompoundPacket(RTCPCompoundPacket *p)
{
RTPTime t = RTPTime::CurrentTime();
printf("%u.%06u SENDING\n",t.GetSeconds(),t.GetMicroSeconds());
DumpCompoundPacket(stdout,p);
}
void DumpCompoundPacket(FILE *f, RTCPCompoundPacket *p)
{
RTCPPacket *pack;
p->GotoFirstPacket();
while ((pack = p->GetNextPacket()) != 0)
{
if (pack->GetPacketType() == RTCPPacket::SR)
{
RTCPSRPacket *p = (RTCPSRPacket *)pack;
RTPTime t(p->GetNTPTimestamp());
fprintf(f," SR packet\n SSRC %27u\n",p->GetSenderSSRC());
fprintf(f," NTP timestamp: %10u.%06u\n RTP timestamp: %17u\n Packets sent: %18u\n Octets sent: %19u\n",t.GetSeconds(),t.GetMicroSeconds(),p->GetRTPTimestamp(),p->GetSenderPacketCount(),p->GetSenderOctetCount());
for (int i = 0 ; i < p->GetReceptionReportCount() ; i++)
fprintf(f," RR block %d\n SSRC %25u\n Fraction lost: %15d\n Packets lost: %16d\n Ext. high. seq. nr: %10u\n Jitter: %22u\n LSR: %25u\n DLSR: %24u\n",(i+1),
p->GetSSRC(i),(int)p->GetFractionLost(i),p->GetLostPacketCount(i),p->GetExtendedHighestSequenceNumber(i),p->GetJitter(i),p->GetLSR(i),
p->GetDLSR(i));
}
else if (pack->GetPacketType() == RTCPPacket::RR)
{
RTCPRRPacket *p = (RTCPRRPacket *)pack;
fprintf(f," RR packet\n SSRC %27u\n",p->GetSenderSSRC());
for (int i = 0 ; i < p->GetReceptionReportCount() ; i++)
fprintf(f," RR block %d\n SSRC %25u\n Fraction lost: %15d\n Packets lost: %16d\n Ext. high. seq. nr: %10u\n Jitter: %22u\n LSR: %25u\n DLSR: %24u\n",(i+1),
p->GetSSRC(i),(int)p->GetFractionLost(i),p->GetLostPacketCount(i),p->GetExtendedHighestSequenceNumber(i),p->GetJitter(i),p->GetLSR(i),
p->GetDLSR(i));
}
else if (pack->GetPacketType() == RTCPPacket::SDES)
{
RTCPSDESPacket *p = (RTCPSDESPacket *)pack;
char str[1024];
if (!p->GotoFirstChunk())
return;
do
{
fprintf(f," SDES Chunk:\n");
fprintf(f," SSRC: %26u\n",p->GetChunkSSRC());
if (p->GotoFirstItem())
{
do
{
switch (p->GetItemType())
{
case RTCPSDESPacket::None:
strcpy(str,"None ");
break;
case RTCPSDESPacket::CNAME:
strcpy(str,"CNAME: ");
break;
case RTCPSDESPacket::NAME:
strcpy(str,"NAME: ");
break;
case RTCPSDESPacket::EMAIL:
strcpy(str,"EMAIL: ");
break;
case RTCPSDESPacket::PHONE:
strcpy(str,"PHONE: ");
break;
case RTCPSDESPacket::LOC:
strcpy(str,"LOC: ");
break;
case RTCPSDESPacket::TOOL:
strcpy(str,"TOOL: ");
break;
case RTCPSDESPacket::NOTE:
strcpy(str,"NOTE: ");
break;
case RTCPSDESPacket::PRIV:
strcpy(str,"PRIV: ");
break;
case RTCPSDESPacket::Unknown:
default:
strcpy(str,"Unknown ");
}
fprintf(f," %s",str);
if (p->GetItemType() != RTCPSDESPacket::PRIV)
{
char str[1024];
memcpy(str,p->GetItemData(),p->GetItemLength());
str[p->GetItemLength()] = 0;
fprintf(f,"%24s\n",str);
}
} while (p->GotoNextItem());
}
} while (p->GotoNextChunk());
}
else if (pack->GetPacketType() == RTCPPacket::BYE)
{
fprintf(f," BYE packet:\n");
RTCPBYEPacket *p = (RTCPBYEPacket *)pack;
int num = p->GetSSRCCount();
int i;
for (i = 0 ; i < num ; i++)
fprintf(f," SSRC: %26u\n",p->GetSSRC(i));
if (p->HasReasonForLeaving())
{
char str[1024];
memcpy(str,p->GetReasonData(),p->GetReasonLength());
str[p->GetReasonLength()] = 0;
fprintf(f," Reason: %24s\n",str);
}
}
}
fprintf(f,"\n");
}
};
int main(int argc, char *argv[])
{
if (argc != 4)
{
fprintf(stderr, "Usage: rtcpdump portbase destIP destport\n");
return -1;
}
int portBase = atoi(argv[1]);
int destPort = atoi(argv[3]);
std::string destIP(argv[2]);
RTPIPv4Address dest;
RTPUDPv4TransmissionParams transParams;
RTPSessionParams sessParams;
MyRTPSession session;
dest.SetIP(ntohl(inet_addr(destIP.c_str())));
dest.SetPort((uint16_t)destPort);
transParams.SetPortbase((uint16_t)portBase);
transParams.SetRTPReceiveBuffer(1024*1024);
transParams.SetRTCPReceiveBuffer(1024*1024);
sessParams.SetOwnTimestampUnit(1.0/9000.0);
sessParams.SetProbationType(RTPSources::NoProbation);
int status;
status = session.Create(sessParams, &transParams);
checkError(status);
status = session.AddDestination(dest);
checkError(status);
int i = 0;
getchar();
return 0;
}