Análisis del protocolo RTP de transmisión de red 16.H264

Uno: protocolo RTP:

1. El protocolo RTP se
compone de dos partes: el protocolo de transporte en tiempo real RTP (Realtime Transport Protocol) y el protocolo de control de transporte en tiempo real RTCP (Realtime Transport Control Protocol);

2. El protocolo RTP proporciona a los usuarios servicios de transmisión en tiempo real de datos de medios continuos basados ​​en redes de multidifusión o unidifusión; el
protocolo RTCP es la parte de control del protocolo RTP, que se utiliza para el monitoreo en tiempo real de la calidad de la transmisión de datos y proporciona control de congestión. y control de flujo para el sistema;

3. Paquete de datos RTP: consta de dos partes: encabezado fijo RTP (encabezado) y carga útil (carga útil);
entre ellos, el significado de los primeros 12 bytes del encabezado es fijo y los datos de carga útil pueden ser datos de audio o video.

2: Análisis del encabezado RTP en la transmisión de video H264:

/*
	RTP数据头信息:12字节	
*/
typedef struct _RTP_FIXED_HEADER
{
    
    
    /**//* 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 payload:7;        /**//* RTP_PAYLOAD_RTSP */
    unsigned char marker:1;        /**//* expect 1 */
	
    /**//* bytes 2, 3 */
    unsigned short seq_no; 
	
    /**//* bytes 4-7 */
    unsigned  long timestamp;  
	
    /**//* bytes 8-11 */
    unsigned long ssrc;            /**//* stream number is used here. */
} __PACKED__ RTP_FIXED_HEADER;

Inserte la descripción de la imagen aquí
1. Descripción del parámetro:
** El byte 0
V:
número de versión del protocolo RTP, que ocupa 2 bits; el
número de versión del protocolo actual es 2 (de acuerdo con el protocolo RFC3984, el número de versión RTP utilizado actualmente debe establecerse en 0x10);

P:
identificador de relleno, que ocupa 1 bit:
si uno o más octetos adicionales se completan al final del mensaje y no forman parte de la cabida útil;

X:
bandera extendida, ocupa 1 bit, si X = 1,
entonces un encabezado extendido es seguido por el encabezado RTP;

CC:
Contador CSRC, que ocupa 4 bits,
indica el número de identificadores CSRC;

** El primer byte
M:
bit de marcador, que ocupa 1 bit.
Si la NALU actual es la última NALU de una unidad de acceso, entonces la M se establece en 1;
o cuando el paquete RTP actual es el último fragmento de una NALU, la M se establece en 1.
En otros casos, el bit M permanece en 0.

PT:
tipo de carga útil, 7 bits;
para el formato de video H.264, actualmente no hay un valor de PT predeterminado especificado, por lo que se puede usar un valor mayor que 95. Aquí, se puede establecer en 0x60 (decimal 96).

** Byte 2-3
SQ:
número de secuencia, 16 bits;
el valor inicial del número de secuencia es un valor aleatorio aquí, establecido en 0, cada vez que se envía un paquete RTP, el valor del número de secuencia aumenta en 1.

** El
TS del 4 ° al 7 ° byte :
Marca de tiempo, 32 bits, 4 bytes; al
igual que el número de serie, el valor inicial de la marca de tiempo también es un valor aleatorio, aquí establecido en 0.
Según el protocolo RFC3984, corresponde a la hora La frecuencia del reloj debe ser 90000 HZ.

** Bytes 8-11
SSRC:
identificación de la fuente de sincronización, 32 bits, 4 bytes;
SSRC debe generarse aleatoriamente para que no haya dos fuentes de sincronización con el mismo identificador SSRC en la misma sesión RTP.
Solo hay una fuente de sincronización, así que configúrela en 0x12345678;

Nota: Los primeros 12 bytes son los datos básicos del encabezado RTP y la fuente especial CRSC posterior es opcional;

CSRC:
fuente especial: 32 bits, 4 bytes;
bandera extendida X: determina si hay una fuente CSRC especial en el encabezado RTP, si el valor es 1, existe;
Contador CC: determina el número de fuentes CSRC especiales:

Tres: modo de transmisión NALU en la red:

1. Para cada NALU, su tamaño también es diferente según el número que contiene.
En la red, cuando el tamaño del mensaje a transmitir supera la unidad de transmisión máxima MTU (Unidad de transmisión máxima), se producirá la fragmentación de paquetes.

2. El tamaño del mensaje máximo (MTU) que se puede transmitir en un entorno Ethernet es de 1500 bytes;

3. Si el paquete de datos enviado es mayor que el valor de MTU, el paquete de datos se desmontará para su transmisión,
lo que generará muchos fragmentos de paquetes de datos, aumentará la tasa de pérdida de paquetes y reducirá la velocidad de la red.

4. Para la transmisión de video, si el paquete RTP es más grande que el MTU y el mecanismo de protocolo subyacente lo desempaqueta arbitrariamente, puede causar un retraso en la reproducción del reproductor final receptor o incluso una falla en la reproducción normal.
Por lo tanto, para unidades NALU mayores que MTU, se debe desembalar.

5. El protocolo RFC3984 proporciona 3 esquemas de empaquetado RTP diferentes:

a: Paquete NALU único:
solo se encapsula una NALU en un paquete RTP Este esquema se usa generalmente para aquellos con menos de 950 bytes en el protocolo estándar;

b: Paquete de agregación:
encapsula múltiples NALU en un paquete RTP. Este esquema de empaquetado se puede utilizar para NALU más pequeñas para mejorar la eficiencia de transmisión;

c: Unidad de fragmentación:
una NALU se encapsula en varios paquetes RTP y las NALU de más de 950 bytes en el protocolo estándar utilizan este esquema para desempaquetar.

Nota: Por lo general, solo use esquemas ayb, pero rara vez use esquemas b;

Cuatro: descripción de la carga útil RTP (carga útil):

typedef struct _FU_INDICATOR
{
    
    
    //byte 0
    unsigned char TYPE:5;
	unsigned char NRI:2; 
	unsigned char F:1;    
	
}__PACKED__ FU_INDICATOR; /**//* 1 BYTES */


typedef struct _FU_HEADER
{
    
    
   	//byte 0
    unsigned char TYPE:5;
	unsigned char R:1;
	unsigned char E:1;
	unsigned char S:1;    
} __PACKED__ FU_HEADER; /**//* 1 BYTES */

Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí
1. Para un solo RTP encapsulado en una NALU:

	RTP有效载荷:载荷头Indicator + NALU数据;(这里的NALU数据指的是h264格式里的0x000001开头的数据);

完整的RTP数据 = RTP头(12字节) + 载荷头Indicator(1字节) + NALU数据(一帧h.264的数据长度)

2. Para una NALU encapsulada en varios paquetes RTP:

	RTP有效载荷:载荷头Indicator + 分片信息fu_header + NALU数据;(这里的NALU数据指的是分片的h264数据);

完整的RTP数据 = RTP头(12字节) + 载荷头Indicator(1字节)  + 分片信息fu_header(1字节) + NALU数据(一帧h.264分片后的数据长度)

Cinco: código de paquete de grupo RTP:


#define nalu_sent_len        	950
#define RTP_H264             	96
#define RTP_AUDIO            	97
#define timestamp_increse        	(90000 / 25)		//90KHz (1s 25帧) == 3600

int VENC_Sent(char *buffer,int buflen)
{
    
    
    int i;
	int is=0;
	int nChanNum=0;
	int ret = 0;

	RTP_FIXED_HEADER *rtp_hdr;
	FU_INDICATOR	 *fu_ind;
	FU_HEADER		 *fu_hdr;

	char *nalu_payload = NULL;
	int nAvFrmLen = 0;
	int nIsIFrm = 0;
	int nNaluType = 0;
	char sendbuf[500*1024+32] = {
    
    0};
	int	bytes = 0;
	char fixHeader[4] = {
    
    0x0A,0x0A,0x0A,0x0A};		
	static int frame_count = 0;

	for(is = 0; is < MAX_RTSP_CLIENT; is ++)
	{
    
    
		if(g_rtspClients[is].status != RTSP_SENDING)
		{
    
    
		    continue;
		}
		
		nAvFrmLen = buflen;
		struct sockaddr_in server;
		server.sin_family = AF_INET;
	   	server.sin_port = htons(g_rtspClients[is].rtpport[0]);          
	   	server.sin_addr.s_addr = inet_addr(g_rtspClients[is].IP);

		rtp_hdr = (RTP_FIXED_HEADER*)&sendbuf[0];
		
		rtp_hdr->payload = RTP_H264;
		rtp_hdr->version = 2;
		rtp_hdr->marker  = 0;
		rtp_hdr->ssrc    = htonl(10);
		
		if(nAvFrmLen <= nalu_sent_len)		//单包发送
		{
    
    
			rtp_hdr->marker = 1;
			rtp_hdr->seq_no = htons(g_rtspClients[is].seqnum++); 

			fu_ind = (FU_INDICATOR*)&sendbuf[12]; 
			fu_ind->F = 0; 
			fu_ind->NRI = nIsIFrm; 
			fu_ind->TYPE = nNaluType;

			nalu_payload = &sendbuf[13];	//未分片:头信息一共占13字节
			memcpy(nalu_payload, buffer, nAvFrmLen);
            g_rtspClients[is].tsvid = g_rtspClients[is].tsvid + timestamp_increse;            
			rtp_hdr->timestamp = htonl(g_rtspClients[is].tsvid);
			bytes = nAvFrmLen + 13 ;
			
			sendto(udpfd, sendbuf, bytes, 0, (struct sockaddr *)&server,sizeof(server));
		}
		else if(nAvFrmLen > nalu_sent_len)	// 分包发送
		{
    
    
			//printf("[%s:%d]:[yang] 222 nAvFrmLen = %d\n",__FUNCTION__,__LINE__,nAvFrmLen);
			int k = 0, l = 0;
			k = nAvFrmLen / nalu_sent_len;	//整包数的个数
			l = nAvFrmLen % nalu_sent_len;	//最后不足一包的数据的字节数
			int t = 0;        

            g_rtspClients[is].tsvid = g_rtspClients[is].tsvid + timestamp_increse;
            rtp_hdr->timestamp = htonl(g_rtspClients[is].tsvid);            

			while(t <= k)
			{
    
    
				rtp_hdr->seq_no = htons(g_rtspClients[is].seqnum++);
				
				if(t == 0)		//
				{
    
    
					rtp_hdr->marker = 0;
					
					fu_ind =(FU_INDICATOR*)&sendbuf[12];
					fu_ind->F = 0; 
					fu_ind->NRI = nIsIFrm;
					fu_ind->TYPE = 28;					
	
					fu_hdr = (FU_HEADER*)&sendbuf[13];	//13
					fu_hdr->E = 0;
					fu_hdr->R = 0;
					fu_hdr->S = 1;
					fu_hdr->TYPE = nNaluType;

					nalu_payload = &sendbuf[14];
					memcpy(nalu_payload,buffer,nalu_sent_len);
					bytes = nalu_sent_len + 14;	//分片数据:头信息一共占14字节
					sendto(udpfd, sendbuf, bytes, 0, (struct sockaddr *)&server,sizeof(server));
					t++;
	
				}
				else if(k == t)	//发送最后不足一包的数据
				{
    
    
					rtp_hdr->marker = 1;
					
					fu_ind =(FU_INDICATOR*)&sendbuf[12]; 
					fu_ind->F= 0 ;
					fu_ind->NRI= nIsIFrm ;
					fu_ind->TYPE=28;

					fu_hdr =(FU_HEADER*)&sendbuf[13];	// 13
					fu_hdr->R = 0;
					fu_hdr->S = 0;
					fu_hdr->TYPE = nNaluType;
					fu_hdr->E = 1;
					nalu_payload = &sendbuf[14];
					memcpy(nalu_payload,buffer + t*nalu_sent_len, l);
					bytes = l + 14;	//分片数据:头信息一共占14字节

					sendto(udpfd, sendbuf, bytes, 0, (struct sockaddr *)&server,sizeof(server));
					t++;
				}
				else if(t < k && t != 0)	//发送 1400大小的数据包
				{
    
    
					rtp_hdr->marker=0;
					
					fu_ind = (FU_INDICATOR*)&sendbuf[12]; 
					fu_ind->F = 0; 
					fu_ind->NRI = nIsIFrm;
					fu_ind->TYPE = 28;					
					fu_hdr = (FU_HEADER*)&sendbuf[13];	// 13
					fu_hdr->R = 0;
					fu_hdr->S = 0;
					fu_hdr->E = 0;
					fu_hdr->TYPE = nNaluType;
					
					nalu_payload = &sendbuf[14];	
					memcpy(nalu_payload, buffer + t*nalu_sent_len, nalu_sent_len);
					bytes = nalu_sent_len + 14;	//分片数据:头信息一共占14字节
					sendto(udpfd, sendbuf, bytes, 0, (struct sockaddr *)&server,sizeof(server));
					t++;
				}
			}
		}

	}
}


Explicación:
1. El marcador (M) del último paquete de r datos enviados en un solo paquete o subpaquete es 1 y el resto es 0;

rtp_hdr->marker = 1;

2. La marca de tiempo tiene un aumento fijo de 3600 en comparación con el marco anterior: es decir, (90000Hz / 25 == 3600)

g_rtspClients[is].tsvid = g_rtspClients[is].tsvid + timestamp_increse;
rtp_hdr->timestamp = htonl(g_rtspClients[is].tsvid); 

Nota: La marca de tiempo debe convertirse al orden de bytes de red htonl (modo big endian);

3. Al enviar una NALU en fragmentos:

fu_hdr->S = 1;	//是否为分片的第一包数据,是则为1				
fu_hdr->R = 0;
fu_hdr->E = 0;	//是否为分片的最后一包数据,是则为1	

Nota: Para el primer paquete, fu_hdr-> S debe ser 1, y para el
último paquete, fu_hdr-> E debe ser 1 y todos los
otros 3 bits son 0.

4. Tipo de carga útil RTP:
Inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/yanghangwww/article/details/112006433
Recomendado
Clasificación