首页  编辑  

RTP转H264 视频流媒体处理

Tags: /超级猛料/Picture.图形图像编程/   Date Created:
#include <stdio.h>
#include <pcap.h>
#include "remote-ext.h"
#include "cfg.h"

#define RTP2H264_VERSION "1.2"
#define  MAXDATASIZE (1500)

FILE * g_outFd =  NULL;    
char * g_outputFile = NULL;
int g_naluNum = 0;
int g_destPort = -1;
int g_srcPort = -1;
char *g_inputFile = NULL;
int g_payloadType= -1;
int g_rawRtpData = -1;
int g_writeLen = 0;             
int g_rtpNum = 0;

/* 4 bytes IP address */
typedef struct ip_address{
u_char byte1;
u_char byte2;
u_char byte3;
u_char byte4;
}ip_address;


struct ether_hdr {
char ether_dst[6];
char ether_src[6];
u_int16_t ether_type; /* we only need the type, so we can determine, if the next header is IPv4 or IPv6 */
};

/* IPv4 header */
typedef struct ip_header{
u_char ver_ihl; // Version (4 bits) + Internet header length (4 bits)
u_char tos; // Type of service 
u_short tlen; // Total length 
u_short identification; // Identification
u_short flags_fo; // Flags (3 bits) + Fragment offset (13 bits)
u_char ttl; // Time to live
u_char proto; // Protocol
u_short crc; // Header checksum
ip_address saddr; // Source address
ip_address daddr; // Destination address
u_int op_pad; // Option + Padding
}ip_header;

/* UDP header*/
typedef struct udp_header{
u_short sport; // Source port
u_short dport; // Destination port
u_short len; // Datagram length
u_short crc; // Checksum
}udp_header;

typedef struct
{
unsigned char version;              //!< Version, 2 bits, MUST be 0x2
unsigned char padding;                 //!< Padding bit, Padding MUST NOT be used
unsigned char extension;            //!< Extension, MUST be zero
unsigned char cc;                      //!< CSRC count, normally 0 in the absence of RTP mixers         
unsigned char marker;                   //!< Marker bit
unsigned char pt;                       //!< 7 bits, Payload Type, dynamically established
unsigned int seq_no;                   //!< RTP sequence number, incremented by one for each sent packet 
unsigned int timestamp;           //!< timestamp, 27 MHz for H.264
unsigned int ssrc;               //!< Synchronization Source, chosen randomly
unsigned char * payload;      //!< the payload including payload headers
unsigned int paylen;           //!< length of payload in bytes
} RTPpacket_t;

typedef struct 
{
/*  0                   1                   2                   3
1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X|  CC   |M|     PT      |       sequence number         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           timestamp                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           synchronization source (SSRC) identifier            |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|            contributing source (CSRC) identifiers             |
|                             ....                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
//intel 的cpu 是intel为小端字节序(低端存到底地址) 而网络流为大端字节序(高端存到低地址)
/*intel 的cpu : 高端->csrc_len:4 -> extension:1-> padding:1 -> version:2 ->低端
在内存中存储 :
低->4001(内存地址)version:2
4002(内存地址)padding:1
4003(内存地址)extension:1
高->4004(内存地址)csrc_len:4

网络传输解析 : 高端->version:2->padding:1->extension:1->csrc_len:4->低端  (为正确的文档描述格式)

存入接收内存 :
低->4001(内存地址)version:2
4002(内存地址)padding:1
4003(内存地址)extension:1
高->4004(内存地址)csrc_len:4
本地内存解析 :高端->csrc_len:4 -> extension:1-> padding:1 -> version:2 ->低端 ,
即:
unsigned char csrc_len:4;        // expect 0 
unsigned char extension:1;       // expect 1
unsigned char padding:1;         // expect 0 
unsigned char version:2;         // expect 2 
*/
/* byte 0 */
unsigned char csrc_len:4;        /* expect 0 */
unsigned char extension:1;       /* expect 1, see RTP_OP below */
unsigned char padding:1;         /* expect 0 */
unsigned char version:2;         /* expect 2 */
/* byte 1 */
unsigned char payloadtype:7;     /* RTP_PAYLOAD_RTSP */
unsigned char marker:1;          /* expect 1 */
/* bytes 2,3 */
unsigned int seq_no;            
/* bytes 4-7 */
unsigned int timestamp;        
/* bytes 8-11 */
unsigned int ssrc;              /* stream number is used here. */
} RTP_FIXED_HEADER;

typedef struct
{
unsigned char forbidden_bit;           //! Should always be FALSE
unsigned char nal_reference_idc;       //! NALU_PRIORITY_xxxx
unsigned char nal_unit_type;           //! NALU_TYPE_xxxx  
unsigned int startcodeprefix_len;      //! 前缀字节数
unsigned int len;                      //! 包含nal 头的nal 长度,从第一个00000001到下一个000000001的长度
unsigned int max_size;                 //! 做多一个nal 的长度
unsigned char * buf;                   //! 包含nal 头的nal 数据
unsigned int lost_packets;             //! 预留
} NALU_t;

/*
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI|  Type   |
+---------------+
*/
typedef struct 
{
//byte 0
unsigned char TYPE:5;
unsigned char NRI:2;
unsigned char F:1;        
} NALU_HEADER; // 1 BYTE 

/*
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI|  Type   |
+---------------+
*/
typedef struct 
{
//byte 0
unsigned char TYPE:5;
unsigned char NRI:2; 
unsigned char F:1;              
} FU_INDICATOR; // 1 BYTE 

/*
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|S|E|R|  Type   |
+---------------+
*/
typedef struct 
{
//byte 0
unsigned char TYPE:5;
unsigned char R:1;
unsigned char E:1;
unsigned char S:1;    
} FU_HEADER;   // 1 BYTES 

NALU_t *AllocNALU(int buffersize)
{
NALU_t *n;

if ((n = (NALU_t*)calloc (1, sizeof(NALU_t))) == NULL)
{
printf("AllocNALU Error: Allocate Meory To NALU_t Failed ");
exit(0);
}
return n;
}

void FreeNALU(NALU_t *n)
{
if (n)
{
free (n);
}
}

/*
*bufIn:rtppackage
*len: the lengthe of rtppackage
*/
void rtp_unpackage(char *bufIn,int len)
{
unsigned char recvbuf[MAXDATASIZE]={0};
RTPpacket_t *p = NULL; 
RTP_FIXED_HEADER * rtp_hdr = NULL;
NALU_HEADER * nalu_hdr = NULL;
NALU_t * n  = NULL;
FU_INDICATOR    *fu_ind = NULL;
FU_HEADER        *fu_hdr= NULL;
int fwrite_number = 0;               //存入文件的数据长度
unsigned int seqno;
int version;

if( len <= 12 ) /* rtp header len > 12 */
{
return ;
}
memcpy(recvbuf,bufIn, len);          //复制rtp包  
//printf("包长度+ rtp头:   = %d\n",len);

//////////////////////////////////////////////////////////////////////////
//begin rtp_payload and rtp_header

p = (RTPpacket_t*)&recvbuf[0];
if ((p = (RTPpacket_t*)malloc (sizeof (RTPpacket_t)))== NULL)
{
printf ("RTPpacket_t MMEMORY ERROR\n");
}
if ((p->payload = (unsigned char *)malloc (MAXDATASIZE))== NULL)
{
printf ("RTPpacket_t payload MMEMORY ERROR\n");
}

rtp_hdr =(RTP_FIXED_HEADER*)&recvbuf[0]; 
//printf("版本号     : %d\n",rtp_hdr->version);

seqno = ntohl(recvbuf[0]);
p->version  = rtp_hdr->version;

/* Check RTP version */
version = (seqno & 0xC0000000) >> 30;
if(version != 2)
{
goto breakoff;
}

p->padding  = rtp_hdr->padding;
p->extension  = rtp_hdr->extension;
p->cc = rtp_hdr->csrc_len;
//printf("标志位     : %d\n",rtp_hdr->marker);
p->marker = rtp_hdr->marker;
//printf("负载类型    :%d\n",rtp_hdr->payloadtype);
p->pt = rtp_hdr->payloadtype;

if( g_payloadType != p->pt)
{
goto breakoff;
}
g_rtpNum++;

//printf("包号       : %d \n",rtp_hdr->seq_no);
p->seq_no = rtp_hdr->seq_no;
//printf("时间戳     : %d\n",rtp_hdr->timestamp);
p->timestamp = rtp_hdr->timestamp;
//printf("帧号       : %d\n",rtp_hdr->ssrc);
p->ssrc = rtp_hdr->ssrc;


if(g_rawRtpData > 0) /* RTP raw data write */
{
memcpy(p->payload,&recvbuf[12],len-12);    
p->paylen = len-12;
if( p->paylen > MAXDATASIZE)
printf("write len %d too large\n",p->paylen);
if( p->paylen > 0)
{
fwrite_number = fwrite(p->payload,1,p->paylen,g_outFd);    //写RTP数据
}
g_writeLen += p->paylen;

goto breakoff;
}

//end rtp_payload and rtp_header
//////////////////////////////////////////////////////////////////////////
//begin nal_hdr
if (!(n = AllocNALU(800000)))          //为结构体nalu_t及其成员buf分配空间。返回值为指向nalu_t存储空间的指针
{
printf("NALU_t MMEMORY ERROR\n");
}

nalu_hdr =(NALU_HEADER*)&recvbuf[12];  //网络传输过来的字节序 ,当存入内存还是和文档描述的相反,只要匹配网络字节序和文档描述即可传输正确。
//printf("forbidden_zero_bit: %d\n",nalu_hdr->F); //网络传输中的方式为:F->NRI->TYPE.. 内存中存储方式为 TYPE->NRI->F (和nal头匹配)。
n->forbidden_bit= nalu_hdr->F << 7;               //内存中的字节序。
//printf("nal_reference_idc:  %d\n",nalu_hdr->NRI);
n->nal_reference_idc = nalu_hdr->NRI << 5;                      
//printf("nal 负载类型:       %d\n",nalu_hdr->TYPE);
n->nal_unit_type = nalu_hdr->TYPE;

//end nal_hdr
//////////////////////////////////////////////////////////////////////////
//开始解包
if ( nalu_hdr->TYPE  == 0)
{
printf("这个包有错误,0无定义\n");
}
else if ( nalu_hdr->TYPE >0 &&  nalu_hdr->TYPE < 24)  //单包
{
// printf("当前包为单包\n");
putc(0x00, g_outFd);
putc(0x00, g_outFd);
putc(0x00, g_outFd);
putc(0x01, g_outFd);    //写进起始字节0x00000001
g_writeLen +=4;
memcpy(p->payload,&recvbuf[13],len-13);    
p->paylen = len-13;
fwrite(nalu_hdr,1,1,g_outFd);    //写NAL_HEADER
g_writeLen += 1;
if(p->paylen > 0)
fwrite_number = fwrite(p->payload,1,p->paylen,g_outFd);    //写NAL数据
g_writeLen += p->paylen;
//printf("包长度 + nal= %d\n",total_bytes);
g_naluNum++;
}
else if ( nalu_hdr->TYPE == 24)                    //STAP-A   单一时间的组合包
{
printf("当前包为STAP-A\n");
}
else if ( nalu_hdr->TYPE == 25)                    //STAP-B   单一时间的组合包
{
printf("当前包为STAP-B\n");
}
else if (nalu_hdr->TYPE == 26)                     //MTAP16   多个时间的组合包
{
printf("当前包为MTAP16\n");
}
else if ( nalu_hdr->TYPE == 27)                    //MTAP24   多个时间的组合包
{
printf("当前包为MTAP24\n");
}
else if ( nalu_hdr->TYPE == 28)                    //FU-A分片包,解码顺序和传输顺序相同
{
g_naluNum++;
if ((fu_ind = (FU_INDICATOR *)malloc(sizeof(FU_INDICATOR))) == NULL)
{
printf("FU_INDICATOR MEMORY ERROR\n");
}
if ((fu_hdr = (FU_HEADER *)malloc(sizeof(FU_HEADER))) == NULL)
{
printf("FU_HEADER MEMORY ERROR\n");
}

fu_ind=(FU_INDICATOR*)&recvbuf[12];        //分片包用的是FU_INDICATOR而不是NALU_HEADER
//printf("FU_INDICATOR->F     :%d\n",fu_ind->F);
n->forbidden_bit = fu_ind->F << 7;
//printf("FU_INDICATOR->NRI   :%d\n",fu_ind->NRI);
n->nal_reference_idc = fu_ind->NRI << 5;                      
//printf("FU_INDICATOR->TYPE  :%d\n",fu_ind->TYPE);
n->nal_unit_type = fu_ind->TYPE;

fu_hdr=(FU_HEADER*)&recvbuf[13];        //FU_HEADER赋值
//printf("FU_HEADER->S        :%d\n",fu_hdr->S);
//printf("FU_HEADER->E        :%d\n",fu_hdr->E);
//printf("FU_HEADER->R        :%d\n",fu_hdr->R);
//printf("FU_HEADER->TYPE     :%d\n",fu_hdr->TYPE);
n->nal_unit_type = fu_hdr->TYPE;               //应用的是FU_HEADER的TYPE

if (rtp_hdr->marker == 1)                      //分片包最后一个包
{
//printf("当前包为FU-A分片包最后一个包\n");
memcpy(p->payload,&recvbuf[14],len - 14);
p->paylen = len - 14;
if(p->paylen > 0)
fwrite_number = fwrite(p->payload,1,p->paylen,g_outFd);    //写NAL数据
g_writeLen += p->paylen;
//printf("包长度 + FU = %d\n",total_bytes);    
}else if (rtp_hdr->marker == 0)                 //分片包 但不是最后一个包
{
if (fu_hdr->S == 1)                        //分片的第一个包
{
unsigned char F;
unsigned char NRI;
unsigned char TYPE;
unsigned char nh;
//printf("当前包为FU-A分片包第一个包\n");
putc(0x00, g_outFd);
putc(0x00, g_outFd);
putc(0x00, g_outFd);
putc(0x01, g_outFd);                //写起始字节码0x00000001
g_writeLen += 4;

F = fu_ind->F << 7;
NRI = fu_ind->NRI << 5;
TYPE = fu_hdr->TYPE;                                            //应用的是FU_HEADER的TYPE
//nh = n->forbidden_bit|n->nal_reference_idc|n->nal_unit_type;  //二进制文件也是按 大字节序存储
nh = F | NRI | TYPE;

putc(nh,g_outFd);                //写NAL HEADER
g_writeLen +=1;

memcpy(p->payload,&recvbuf[14],len - 14);
p->paylen = len - 14;
if(p->paylen > 0)
fwrite_number = fwrite(p->payload,1,p->paylen,g_outFd);    //写NAL数据
g_writeLen += p->paylen;
//printf("包长度 + FU_First = %d\n",total_bytes);    
} else                                      //如果不是第一个包
{
//printf("当前包为FU-A分片包\n");
memcpy(p->payload,&recvbuf[14],len - 14);
p->paylen= len - 14;
if(p->paylen > 0)
fwrite_number = fwrite(p->payload,1,p->paylen,g_outFd);    //写NAL数据
g_writeLen += p->paylen;
// printf("包长度 + FU = %d\n",total_bytes);    
}    
}
}else if ( nalu_hdr->TYPE == 29)                //FU-B分片包,解码顺序和传输顺序相同
{
if (rtp_hdr->marker == 1)                  //分片包最后一个包
{
printf("当前包为FU-B分片包最后一个包\n");

}else if (rtp_hdr->marker == 0)             //分片包 但不是最后一个包
{
printf("当前包为FU-B分片包\n");
}
} else
{
printf("Unknown NALU Type %d\n",nalu_hdr->TYPE);
}

breakoff:
if(NULL != p)
{
if( NULL != p->payload)
{
free (p->payload);
p->payload = NULL;
}
free (p);
p = NULL;
}
if(NULL!= n)
{
FreeNALU(n);
n=NULL;
}
//结束解包
//////////////////////////////////////////////////////////////////////////
return ;
}

/* Callback function invoked by libpcap for every incoming packet */
/*
pkt_tlen = ip_tlen + eth_hlen(14)
ip_tlen =  ip_hlen + udp_tlen
udp_tlen = udp_hlen + udp data len
*/
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
ip_header *ih;
udp_header *uh;
u_int ip_hlen; /* ip header len */
u_int ip_tlen; /* ip total len */
u_int udp_tlen; /* udp total len */
u_int udp_dlen; /* udp data len */
u_int pkt_tlen; /* packet total len */
u_int cap_tlen; /* 实际报文长度,可能与pkt_tlen不一致,tcpdump时默认抓取长度pkt_tlen=96? */
u_int cap_udplen; /* 实际udp长度 */
u_short sport,dport;
struct ether_hdr *ethhdr = NULL;

/*
* unused parameter
*/
(VOID)(param);

if(NULL == pkt_data || NULL == header)
{
return; 
}

ethhdr = (struct ether_hdr *)pkt_data;
pkt_tlen = header->len;
cap_tlen = header->caplen;

if (ntohs(ethhdr->ether_type) != 0x0800 
|| pkt_tlen <= 0 || pkt_tlen > MAXDATASIZE)/* IPv4 */ 
{
//printf("Ignoring non IPv4 packet!\n");
return;
}

/* retireve the position of the ip header */
ih = (ip_header *) (pkt_data +
14); //length of ethernet header

if(17 != ih->proto) /* not udp */
{
return ;
}

ip_hlen = (ih->ver_ihl & 0xf) * 4;
ip_tlen = ntohs( ih->tlen );

/* retireve the position of the udp header */
uh = (udp_header *) ((u_char*)ih + ip_hlen);
udp_tlen = (u_long)(ntohs(uh->len));
udp_dlen = udp_tlen-8;
cap_udplen = cap_tlen -14 -ip_hlen -8;

if( pkt_tlen != ip_tlen+14 || 
ip_tlen != ip_hlen+udp_tlen 
|| udp_tlen > MAXDATASIZE
|| udp_tlen <= 0 
|| cap_udplen <= 0)
{
return ;
}

/* convert from network byte order to host byte order */
sport = ntohs( uh->sport );
dport = ntohs( uh->dport );

if(g_destPort >0 && dport != g_destPort )
{
return ;
}
if(g_srcPort > 0 && sport != g_srcPort)
{
return ;
}

/* print ip addresses and udp ports */
/*
printf("%d.%d.%d.%d:%d -> %d.%d.%d.%d:%d len:%d\n",
ih->saddr.byte1,
ih->saddr.byte2,
ih->saddr.byte3,
ih->saddr.byte4,
sport,
ih->daddr.byte1,
ih->daddr.byte2,
ih->daddr.byte3,
ih->daddr.byte4,
dport, udp_len-8);
*/
rtp_unpackage((u_char*)uh+8, cap_udplen);

return;
}

void print_usage ()
{
fprintf (stdout, "\n\
usage:\n  rtp2h264 [-i input_file] [-p payload_type] [-d dest_port] [-s src_port] [-o output_file]\n\
  -i  input_file specify path of input RTP-H264-ES file(default: h264.pcap)\n\
  -o  output_file specify path of output H264-Annexb file(default: out.h264)\n\
  -d  dest_port specify the udp destination port(default: 9080)\n\
  -s  src_port specify the udp soure port(default: 9078)\n\
  -p  payload_type specify the rtp payload type(default: 102)\n\
  -r write raw rtp data\n\
  -h display this usage\n\
  -v display version \n");
}

int parse_cmdline(int argc, char **argv)
{
int arg_num=1;
int parseFlag = 1 ;

while (arg_num < argc)
{
if (strncmp ("-d", argv[arg_num], 2) == 0)
{
arg_num++;

if (arg_num < argc)
g_destPort = atoi (argv[arg_num]);
else
g_destPort = 9080;

}
else if (strncmp ("-s", argv[arg_num], 2) == 0)
{
arg_num++;

if (arg_num < argc)
g_srcPort = atoi (argv[arg_num]);
else
g_srcPort = 9078;
}
else if (strncmp ("-p", argv[arg_num], 2) == 0)
{
arg_num++;

if (arg_num < argc)
g_payloadType = atoi (argv[arg_num]);
else
g_payloadType = 102;
}
else if (strncmp ("-r", argv[arg_num], 2) == 0)
{
g_rawRtpData = 1;
}
else if (strncmp ("-i", argv[arg_num], 2) == 0)
{
arg_num++;
g_inputFile = argv[arg_num];
}
else if (strncmp ("-o", argv[arg_num], 2) == 0)
{
arg_num++;
g_outputFile = argv[arg_num];
}
else if (strncmp ("-h", argv[arg_num], 2) == 0)
{
arg_num++;
parseFlag = 0 ;
}
else if (strncmp ("-v", argv[arg_num], 2) == 0)
{
arg_num++;
parseFlag = 0 ;
printf ("\n  rtp2h264 version: " RTP2H264_VERSION "\n");
}
else
{
parseFlag = 1 ;
}
arg_num++;
}

return parseFlag;
}

int main(int argc, char **argv)
{
pcap_t *fp;
int  parseFlag = -1;
char errbuf[PCAP_ERRBUF_SIZE]={0};
char source[PCAP_BUF_SIZE]={0};
Cfg  *cfg = NULL;

parseFlag = parse_cmdline(argc, argv);
if(parseFlag <= 0 )
{
print_usage();
return 0;
}

cfg=cfg_new("config.ini");
if(NULL == cfg )
{
fprintf(stderr,"\nUnable to open the config file 'config.ini'\n");
return -1;
}
g_inputFile = cfg_get_string(cfg,"rtp","input_file","h264.pcap");
g_outputFile = cfg_get_string(cfg,"rtp","output_file","out.h264");
g_payloadType = cfg_get_int(cfg,"rtp","payload_type",102);
g_srcPort = cfg_get_int(cfg,"rtp","src_port",9078);
g_destPort = cfg_get_int(cfg,"rtp","dest_port",9080);
g_rawRtpData = cfg_get_int(cfg,"rtp","write_raw_rtp_data",0);


/* Create the source string according to the new WinPcap syntax */
if ( pcap_createsrcstr( source, // variable that will keep the source string
PCAP_SRC_FILE, // we want to open a file
NULL, // remote host
NULL, // port on the remote host
g_inputFile, // name of the file we want to open
errbuf // error buffer
) != 0)
{
fprintf(stderr,"\nError creating a source string\n");
return -1;
}


/* Open the capture file */
if ( (fp= pcap_open(source, // name of the device
65536, // portion of the packet to capture
// 65536 guarantees that the whole packet will be captured on all the link layers
PCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode
1000, // read timeout
NULL, // authentication on the remote machine
errbuf // error buffer
) ) == NULL)
{
fprintf(stderr,"\nUnable to open the input file %s\n", g_inputFile);
print_usage();
return -1;
}

//print_usage();
if(g_rawRtpData <= 0 )
{
fprintf(stderr,"\nrtp2h264 -i %s -p %d -d %d -s %d -o %s\n", 
g_inputFile,g_payloadType,g_destPort, g_srcPort, g_outputFile );
}else
{
fprintf(stderr,"\nrtp2h264 -i %s -p %d -d %d -s %d -o %s -r(write raw rtp data) \n", 
g_inputFile,g_payloadType,g_destPort, g_srcPort, g_outputFile );
}

g_outFd = fopen(g_outputFile, "wb");
if (NULL == g_outFd)
{
printf("Error: Open output file %s error\n", g_outputFile);
return -1;
}

// read and dispatch packets until EOF is reached
pcap_loop(fp, 0, packet_handler, NULL);

if(g_rawRtpData <= 0 )
{
fprintf(stderr,"from RTP-H264-ES to H264-Annexb, rtp-package:%d, nalu:%d, total-len:%d\n",g_rtpNum,g_naluNum,g_writeLen);
}else
{
fprintf(stderr,"write rtp raw data total-len:%d\n",g_writeLen);
}

fprintf (stdout, "\n\usage(see 'config.ini'):\n\
-i  input_file specify path of input RTP-H264-ES file('%s')\n\
-o  output_file specify path of output H264-Annexb file('%s')\n\
-d  dest_port specify the udp destination port('%d')\n\
-s  src_port specify the udp soure port('%d')\n\
-p  payload_type specify the rtp payload type('%d')\n\
-r write raw rtp data('%d')\n",
  g_inputFile,g_outputFile,g_destPort,g_srcPort,g_payloadType,g_rawRtpData);

goto_exit:
cfg_destroy(cfg);

return 0;
}
https://blog.csdn.net/bingqingsuimeng/article/details/9997267
RTP包解析示例如下所示:
static int parsingRTPPacket(uint8_t *data, size_t size) {  
    if (size < 12) {  
        //Too short to be a valid RTP header.  
        return -1;  
    }  
  
    if ((data[0] >> 6) != 2) {  
        //Currently, the version is 2, if is not 2, unsupported.  
        return -1;  
    }  
  
    if (data[0] & 0x20) {  
        // Padding present.  
        size_t paddingLength = data[size - 1];  
        if (paddingLength + 12 > size) {  
            return -1;  
        }  
        size -= paddingLength;  
    }  
  
    int numCSRCs = data[0] & 0x0f;  
    size_t payloadOffset = 12 + 4 * numCSRCs;  
  
    if (size < payloadOffset) {  
        // Not enough data to fit the basic header and all the CSRC entries.  
        return -1;  
    }  
  
    if (data[0] & 0x10) {  
        // Header extension present.  
        if (size < payloadOffset + 4) {  
            // Not enough data to fit the basic header, all CSRC entries and the first 4 bytes of the extension header.  
            return -1;  
        }  
  
        const uint8_t *extensionData = &data[payloadOffset];  
        size_t extensionLength = 4 * (extensionData[2] << 8 | extensionData[3]);  
  
        if (size < payloadOffset + 4 + extensionLength) {  
            return -1;  
        }  
        payloadOffset += (4 + extensionLength);  
    }  
  
    uint32_t rtpTime = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7];  
    uint32_t srcId = data[8] << 24 | data[9] << 16 | data[10] << 8 | data[11];  
    uint32_t seqNum = data[2] << 8 | data[3];  
  
    return 0;  
}