OPTIMIZACIÓN DE UMTS PARA SERVICIOS DE VÍDEO STREAMING.
I. Anexo I (Filtro de vídeo)
A continuación se muestra el código implementado para la
clasificación de los paquetes IP.
I.1. Clasificación de los paquetes.
#ifndef _PAQUET_CHEK
#define _PAQUET_CHEK
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
#ifndef _FPAQUETE
#include "fpaquete.h"
#endif
#ifndef vat_packet_h
#include "vat.h"
#endif
#ifndef _FTUN
#include "ftun.h"
#endif
#ifndef _STATISTICS_H_
#include "statistics.h"
#endif
#define LogFILTER 1
#ifndef MAX_TYPE
#define MAX_TYPES 6
#endif
#ifndef TUN_DEVICES
#define TUN_DEVICES 2
#endif
#define TUN_HEADER_SIZE 4
#define PAQ_SIZE 1600
enum {RTP,RTCP,VATC,VATD};
enum {ICMP,UDP,TCP,OTHER};
typedef enum {I,P,B,AUDIO,UNK,R} type_media;
typedef u_int32 member_t;
struct cola
{
int num_paquetes_cola;
int max_bytes_cola;
int max_bytes_bucket;
struct _Paquete *paq;
unsigned long last_ts;
unsigned int last_marker_bit;
float bucket;
int bit_rate;
float ulp;
float p,q;
float clp;
int estado_malo;
int max_paquetes;
int new_frame;
struct stats_cola sts;
};
static struct
{
char *enc; /* encoding name */
int rate; /* sampling rate for audio; clock rate for video */
int ch; /* audio channels; 0 for video */
}
pt_map[256];
static struct
{
rtcp_sdes_type_t t;
char *name;
}
map[] = {
{RTCP_SDES_END, "end"},
{RTCP_SDES_CNAME, "CNAME"},
{RTCP_SDES_NAME, "NAME"},
{RTCP_SDES_EMAIL, "EMAIL"},
{RTCP_SDES_PHONE, "PHONE"},
{RTCP_SDES_LOC, "LOC"},
{RTCP_SDES_TOOL, "TOOL"},
{RTCP_SDES_NOTE, "NOTE"},
{RTCP_SDES_PRIV, "PRIV"},
{11, "SOURCE"},
{0,0}
};
/*void log_Printf(int channel ,char *str)
{
printf("%s\n",str);
}*/
//////////////////
static int parse_control(FILE *out, char *buf, int len);
int PacketCheck_IP(FILE *out,struct _Paquete *Paquete_tun);
type_media parse_type_image(char *buff);
int parse_header(char *buf);
void hex(FILE *out, char *buf, int len);
void set_market_ts(char *buf,struct cola *cola);
int parse_data(FILE *out, char *buf, int len);
int PacketCheck_IP(FILE *out,struct _Paquete *Paquete_tun)
{
int gotinfo; /* true if IP payload decoded */
int cproto; /* P_* protocol type if (gotinfo) */
int estab, syn, finrst; /* TCP state flags if (gotinfo) */
unsigned short sport, dport; /* src, dest port from packet if (gotinfo)
*/
int len; /* bytes used in dbuff */
int didname; /* true if filter header printed */
char dbuff[100];
char *temp=((char *)(Paquete_tun->contenido+TUN_HEADER_SIZE));
struct ip *pip=(struct ip*)temp;
const char *ptop = (const char *) pip + (pip->ip_hl << 2);
int datalen; /* IP datagram length */
/*
* Deny any packet fragment that tries to over-write the header.
* Since we no longer have the real header available, punt on the
* largest normal header - 20 bytes for TCP without options, rounded
* up to the next possible fragment boundary. Since the smallest
* `legal' MTU is 576, and the smallest recommended MTU is 296, any
* fragmentation within this range is dubious at best
*/
Paquete_tun->ip_hdr=(struct ip*)temp;
len = ntohs(pip->ip_off) & IP_OFFMASK; /* fragment offset */
if (len > 0)
{ /* Not first fragment within datagram */
if (len < (24 >> 3))
{ /* don't allow fragment to over-write header */
fprintf(out, " error: illegal header\n");
return OTHER;
}
}
cproto = gotinfo = estab = syn = finrst = didname = 0;
sport = dport = 0;
datalen = ntohs(pip->ip_len) - (pip->ip_hl << 2);
switch (pip->ip_p)
Anexo I (Filtro de vídeo) 60
{
case IPPROTO_ICMP:
cproto = ICMP;
Paquete_tun->IP_type=ICMP;
if (datalen < 8)
{ /* ICMP must be at least 8 octets */
fprintf(out, " error: ICMP must be at least 8 octets\n");
return OTHER;
}
Paquete_tun->icmp_hdr = (struct icmp *) ptop;
sport = Paquete_tun->icmp_hdr->icmp_type;
estab = syn = finrst = -1;
snprintf(dbuff, sizeof dbuff, " ICMP sport = %d", sport);
break;
case IPPROTO_UDP:
cproto = UDP;
if (datalen < 8)
{ /* UDP header is 8 octets */
fprintf(out, " error: UDP must be at least 8 octets\n");
return OTHER;
}
Paquete_tun->IP_type=UDP;
Paquete_tun->longitud=datalen-sizeof(struct udphdr);
Paquete_tun->udp_hdr = (struct udphdr *) ptop;
Paquete_tun->first_data_byte=(char*) ptop + sizeof(struct udphdr);
sport = ntohs(Paquete_tun->udp_hdr->source);
dport = ntohs(Paquete_tun->udp_hdr->dest);
estab = syn = finrst = -1;
snprintf(dbuff, sizeof dbuff, "UDP sport = %d, dport = %d",
sport, dport);
break;
case IPPROTO_TCP:
cproto = TCP;
Paquete_tun->IP_type=TCP;
Paquete_tun->tcp_hdr = (struct tcphdr *) ptop;
/* TCP headers are variable length. The following code
* ensures that the TCP header length isn't de-referenced if
* the datagram is too short
*/
if (datalen < 20 || datalen < (Paquete_tun->tcp_hdr->doff << 2))
{
fprintf(out, " error: TCP header incorrect\n");
return OTHER;
}
Paquete_tun->first_data_byte=(char*) ptop + sizeof(struct tcphdr);
Paquete_tun->longitud=datalen-sizeof(struct tcphdr);
sport = ntohs(Paquete_tun->tcp_hdr->source);
dport = ntohs(Paquete_tun->tcp_hdr->dest);
//estab = (th->th_flags & TH_ACK);
//syn = (th->th_flags & TH_SYN);
//finrst = (th->th_flags & (TH_FIN|TH_RST));
snprintf(dbuff, sizeof dbuff, "TCP sport = %d, dport = %d",
sport, dport);
/*if (!estab)
snprintf(dbuff, sizeof dbuff,
"TCP flags = %02x, sport = %d, dport = %d",
th->th_flags, sport, dport);
else
*dbuff = '\0';*/
break;
default:
Paquete_tun->IP_type=OTHER;
fprintf(out, " error: unknown protocol\n");
return OTHER; /* We'll block unknown type of packet */
}
fprintf(out,"%s\n",dbuff);
return cproto;
}
type_media parse_type_image(char *buff)
{
unsigned char b1=buff[0];
unsigned char b2=buff[1];
unsigned char b3=buff[2];
unsigned char b4=buff[3];
unsigned char b5=(buff[4])&0xC0;
//printf("== %02x %02x %02x %02x %02x ==\n",b1,b2,b3,b4,b5);
if(b1==0x00 && b2==0x00 && b3==0x01 && b4==0xB6)
{
switch(b5)
{
case 0x00:
//printf("tipo I\n");
return I;
case 0x40:
//printf("tipo P\n");
return P;
case 0x80:
printf("tipo B\n");
return B;
default:
//printf("tipo OTHER\n");
return UNK;
}
}
else if (b1==0x00 && b2==0x10 )
{
//printf("tipo Audio\n");
return AUDIO;
}
else return UNK;
}
int parse_type(int type,char *buf)
{
if (type == 0)
{
rtp_hdr_t *r = (rtp_hdr_t *)buf;
return r->version == RTP_VERSION ? RTP : VATD;
}
else
{
rtcp_t *r = (rtcp_t *)buf;
return r->common.version == RTP_VERSION ? RTCP : VATC;
}
} /* parse_type */
/*
* Return header length of RTP packet contained in 'buf'.
*/
int parse_header(char *buf)
{
rtp_hdr_t *r = (rtp_hdr_t *)buf;
int hlen = 0;
if (r->version == 0)
{
vat_hdr_t *v = (vat_hdr_t *)buf;
hlen = 8 + v->nsid * 4;
}
else if (r->version == RTP_VERSION)
{
hlen = 12 + r->cc * 4;
}
return hlen;
} /* parse_header */
void hex(FILE *out, char *buf, int len)
{
int i;
for (i = 0; i < len; i++)
Anexo I (Filtro de vídeo) 62
{
fprintf(out, "%02x", (unsigned char)buf[i]);
}
} /* hex */
void set_market_ts(char *buf,struct cola *cola_k)
{
unsigned long x;
rtp_hdr_t *r = (rtp_hdr_t *)buf;
x=(unsigned long)ntohl(r->ts);
cola_k->last_ts=x;
cola_k->last_marker_bit=(unsigned int)r->m;
//printf("%lu Timestamp, %d marker\n",x,r->m);
}
/*
* Return header length.
*/
int parse_data(FILE *out, char *buf, int len)
{
rtp_hdr_t *r = (rtp_hdr_t *)buf;
rtp_hdr_ext_t *ext;
int i, ext_len=0;
int hlen = 0;
/* Show vat format packets. */
if (r->version == 0)
{
vat_hdr_t *v = (vat_hdr_t *)buf;
fprintf(out, "nsid=%d flags=0x%x confid=%u ts=%u\n",
v->nsid, v->flags, v->confid, v->ts);
hlen = 8 + v->nsid * 4;
buf=buf+hlen+ext_len;
}
else if (r->version == RTP_VERSION)
{
hlen = 12 + r->cc * 4;
if (len < hlen)
{
fprintf(out, "RTP header too short (%d bytes for %d CSRCs).\n",
len, r->cc);
return hlen+ext_len;
}
fprintf(out,
"v=%d p=%d x=%d cc=%d m=%d pt=%d (%s,%d,%d) seq=%u ts=%lu ssrc=0x%lx ",
r->version, r->p, r->x, r->cc, r->m,
r->pt, pt_map[r->pt].enc, pt_map[r->pt].ch, pt_map[r->pt].rate,
ntohs(r->seq),
(unsigned long)ntohl(r->ts),
(unsigned long)ntohl(r->ssrc));
for (i = 0; i < r->cc; i++)
{
fprintf(out, "csrc[%d] = %0lx ", i, r->csrc[i]);
}
if (r->x)
{ /* header extension */
ext = (rtp_hdr_ext_t *)((char *)buf + hlen);
ext_len = ntohs(ext->len);
fprintf(out, "ext_type=0x%x ", ntohs(ext->ext_type));
fprintf(out, "ext_len=%d ", ext_len);
if (ext_len)
{
fprintf(out, "ext_data=");
hex(out,((char *)(ext+1)),(ext_len*4));
}
}
buf=buf+(hlen+ext_len);
hex(out,buf,6);
return hlen+ext_len;
}
else
{
fprintf(out, "RTP version wrong (%d).\n", r->version);
}
return hlen+ext_len;
} /* parse_data */
#endif
I.2. Estadísticas del filtro.
#ifndef STATS
#define STATS
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <sys/time.h>
#include <time.h>
#include <signal.h>
#ifndef MAX_TYPE
#define MAX_TYPES 6
#endif
#ifndef TUN_DEVICES
#define TUN_DEVICES 2
#endif
#define time_to_ms(tv) ((tv)->tv_sec*1000 + (tv)->tv_usec / 1000.0)
#include "statistics.h"
#include "Paquet_check.c"
double actual_time;
double initial_time=0;
struct timeval temp_tv;
FILE *stats_file[TUN_DEVICES][MAX_TYPES];
struct stats_cola *stats;
extern struct cola cola[TUN_DEVICES][MAX_TYPES];
/* Estructura con el contador de tiempo */
struct itimerval contador;
/* Valor inicial del contador */
struct timeval tiempoInicial;
/* Tiempo de repetición del contador */
struct timeval tiempoRepeticion;
void tratamiento_estadistico_final (int);
void tratamiento_estadistico_inst (int);
double get_actual_time()
{
if(initial_time==0)
//INIT
{
gettimeofday(&temp_tv,NULL);
initial_time=time_to_ms(&temp_tv);
}
gettimeofday(&temp_tv,NULL);
actual_time=time_to_ms(&temp_tv);
return actual_time - initial_time;
}
void print_header_inst()
{
printf("T_TIME INC_TIME N_PAQ L_PAQ %%P_FRAG L_P_FRAG %%P_PERD TASA\n");
}
void print_header_inst_FILE(FILE *canal)
{
Anexo I (Filtro de vídeo) 64
fprintf(canal,"T_TIME INC_TIME N_PAQ L_PAQ %%P_FRAG L_P_FRAG %%P_PERD
TASA\n");
fflush(canal);
}
void print_results(int canal,struct stats_cola *st)
{
printf("%.1f %.1f %.0f %.0f %.2f %.2f %.3f %.2f\n",st-
>acumulated_time,st->time_last_computed,st->num_medio_paquetes_inst,st-
>long_media_paq_inst,st->num_medio_paq_fragmentados_inst,st->long_media_frag_inst,st-
>num_medio_paquetes_inst_perdidos,st->bitrate_medio_inst);
}
void print_results_to_file(FILE *canal,struct stats_cola *st)
{
fprintf(canal,"%.1f %.1f %.0f %.0f %.2f %.2f %.3f %.2f\n",st-
>acumulated_time,st->time_last_computed,st->num_medio_paquetes_inst,st-
>long_media_paq_inst,st->num_medio_paq_fragmentados_inst,st->long_media_frag_inst,st-
>num_medio_paquetes_inst_perdidos,st->bitrate_medio_inst);
fflush(canal);
}
void print_results_FINAL_to_file(FILE *canal,struct stats_cola *st)
{
fprintf(canal,"MUESTRAS=%.0f B_RATE=%.2f %%LOST=%3f L_MED_PAQ=%.0f %%P_FRAG=%.2f
L_MEDI_FRAG=%.1f PAQ_TOTALES=%.0f\n",st->num_periodos_muestreados-1,st-
>bitrate_medio,st->num_medio_paquetes_totales_perdidos,st->long_media_paq_total,st-
>num_medio_paq_fragmentados,st->long_media_frag_media,st->num_medio_paquetes_totales);
fflush(canal);
}
void print_results_FINAL(struct stats_cola *st)
{
printf("MUESTRAS=%.0f B_RATE=%.2f %%LOST=%3f L_MED_PAQ=%.0f %%P_FRAG=%.2f
L_MEDI_FRAG=%.1f PAQ_TOTALES=%.0f\n",st->num_periodos_muestreados-1,st-
>bitrate_medio,st->num_medio_paquetes_totales_perdidos,st->long_media_paq_total,st-
>num_medio_paq_fragmentados,st->long_media_frag_media,st->num_medio_paquetes_totales);
}
void init_stats(struct stats_cola *st)
{
st->bitrate_medio=0;
st->bitrate_medio_inst=0;
st->long_media_frag_inst=0;
st->long_media_frag_media=0;
st->long_media_paq_inst=0;
st->long_media_paq_total=0;
st->max_long=0;
st->min_lon=1500;
st->num_medio_paq_fragmentados=0;
st->num_medio_paq_fragmentados_inst=0;
st->num_medio_paquetes_inst_perdidos=0;
st->num_medio_paquetes_totales_perdidos=0;
st->num_medio_paquetes_inst=0;
st->num_medio_paquetes_totales=0;
st->tiempo_entre_llegadas_medio=0;
st->tiempo_entre_llegadas_medio_inst=0;
st->num_periodos_muestreados=1;
st->acumulated_time=0;
if(initial_time==0)
//INIT
{
gettimeofday(&temp_tv,NULL);
initial_time=time_to_ms(&temp_tv);
}
}
void clear_inst(struct stats_cola *st)
{
st->bitrate_medio_inst=0;
st->long_media_frag_inst=0;
st->long_media_paq_inst=0;
st->num_medio_paq_fragmentados_inst=0;
st->num_medio_paquetes_inst_perdidos=0;
st->num_medio_paquetes_inst=0;
st->tiempo_entre_llegadas_medio_inst=0;
}
//TIMER PART
//SET AND START THE TIMER
void init_timer()
{
int tun,u;
char buffer[30];
/* Se rellena el tiempo inicial del contador con 2 segundos */
tiempoInicial.tv_sec=5;
tiempoInicial.tv_usec=0;
/* Se rellena el tiempo de repetición con 5 segundos */
tiempoRepeticion.tv_sec=0;
tiempoRepeticion.tv_usec=5000000;
/* Se rellenan los datos del contador */
contador.it_value=tiempoInicial;
contador.it_interval=tiempoRepeticion;
/* Se cambia el tratamiento de la señal por defecto para que llame a
* nuestra función tratamientoSenhal */
for(tun=0;tun<TUN_DEVICES;tun++)
{
for(u=0;u<MAX_TYPES;u++)
{
sprintf(buffer,"stats_%d_%d.txt",tun,u);
stats_file[tun][u]=fopen(buffer,"w+");
print_header_inst_FILE(stats_file[tun][u]);
}
}
print_header_inst();
signal (SIGALRM, tratamiento_estadistico_inst);
signal (SIGINT,tratamiento_estadistico_final);
/* Se pone en marcha el contador.
* La primera vez tardará 2 segundos en saltar, según indicamos en
* tiempoInicial. Luego saltará automáticamente cada medio segundo, como
* indicamos en tiempoRepeticion. */
setitimer (ITIMER_REAL, &contador, NULL);
}
void tratamiento_estadistico_inst (int signal)
{
int u;
int tun;
printf("\n\n\n");
print_header_inst();
for(tun=0;tun<TUN_DEVICES;tun++)
{
printf("STATISTICS FOR TUN=%d\n",tun);
for(u=0;u<MAX_TYPES;u++)
{
//update_stadistics
stats=&cola[tun][u].sts;
//Add for final calculus
stats->time_last_computed=get_actual_time()-stats->acumulated_time;//-
stats->time_last_computed;
stats->acumulated_time+=stats->time_last_computed;
stats->bitrate_medio_inst=stats->long_media_paq_inst*8/stats-
>time_last_computed;
stats->long_media_frag_inst=stats->long_media_frag_inst/stats-
>num_medio_paq_fragmentados_inst;
stats->long_media_paq_inst=stats->long_media_paq_inst/stats-
>num_medio_paquetes_inst;
stats->num_medio_paq_fragmentados_inst=stats-
>num_medio_paq_fragmentados_inst/stats->num_medio_paquetes_inst;
stats->num_medio_paquetes_inst_perdidos=stats-
>num_medio_paquetes_inst_perdidos/stats->num_medio_paquetes_inst;
//ADD MEANS TO GLOBAL MEANS
Anexo I (Filtro de vídeo) 66
stats->bitrate_medio=(stats->bitrate_medio*(stats-
>num_periodos_muestreados-1) + stats->bitrate_medio_inst)/stats-
>num_periodos_muestreados;
stats->long_media_frag_media=(stats->long_media_frag_media*(stats-
>num_periodos_muestreados-1) + stats->long_media_frag_inst)/stats-
>num_periodos_muestreados;
stats->long_media_paq_total=(stats->long_media_paq_total*(stats-
>num_periodos_muestreados-1) + stats->long_media_paq_inst)/stats-
>num_periodos_muestreados;
stats->num_medio_paq_fragmentados=(stats-
>num_medio_paq_fragmentados*(stats->num_periodos_muestreados-1) + stats-
>num_medio_paq_fragmentados_inst)/stats->num_periodos_muestreados;
stats->num_medio_paquetes_totales_perdidos=(stats-
>num_medio_paquetes_totales_perdidos*(stats->num_periodos_muestreados-1) + stats-
>num_medio_paquetes_inst_perdidos)/stats->num_periodos_muestreados;
stats->num_medio_paquetes_totales+=stats->num_medio_paquetes_inst;
print_results_to_file(stats_file[tun][u],stats);
print_results((int)stdout,stats);
stats->num_periodos_muestreados++;
clear_inst(stats);
}
}
}
void tratamiento_estadistico_final(int signal)
{
int tun,u;
//printf("Muestreados %d periodos\n",stats.num_periodos_muestreados);
printf("HAS APRETADO CRTL+C\n");
for(tun=0;tun<TUN_DEVICES;tun++)
{
printf("STATISTICS FOR TUN=%d\n",tun);
for(u=0;u<MAX_TYPES;u++)
{
stats=&cola[tun][u].sts;
print_results_FINAL_to_file(stats_file[tun][u],stats);
print_results_FINAL(stats);
fclose(stats_file[tun][u]);
}
}
exit (0);
}
I.3. Programa principal.
r=tun_read(tun_paquet.contenido,&l,NON_BLOCKING);
if(l>0)
{
//RECONIGSE PACKET
switch(PacketCheck_IP(out,&tun_paquet))
{
case ICMP:
actual_index=UNK;
break;
case UDP:
if((port=ntohs(tun_paquet.udp_hdr->dest))>1024) //no son puertos reservados
{
switch(parse_type(port%2,((char *)(tun_paquet.first_data_byte))))
{
case RTP:
len=parse_data(out,((char
*)(tun_paquet.first_data_byte)),tun_paquet.longitud);
//hex(out,tun_paquet.first_data_byte+port,6);
actual_index=parse_type_image(((char *)(tun_paquet.first_data_byte+len)));
//can be I,P,B,S or AUDIO
//CHECK IF THE RETURN VALUE WAS UNK BUT IT IS A CONTINUATION OF NEW FRAME
if(actual_index!=UNK)
{
cola[r][actual_index].new_frame=TRUE;
set_market_ts(((char
*)(tun_paquet.first_data_byte)),&cola[r][actual_index]);
}
else
{
//recorrer los tipos I,P,B a ver si estan lo del timestamp
//SI los time stamp son iguales
ultemp=(unsigned long) ntohl(((rtp_hdr_t *)(tun_paquet.first_data_byte))-
>ts);
//printf("watching for %lu",ultemp);
for(k=0;k<MAX_TYPES;k++)
{
ultemp=(unsigned long) ntohl(((rtp_hdr_t
*)(tun_paquet.first_data_byte))->ts);
//printf ("ts %ul for %d type and marker
%d\n",cola[r][k].last_ts,k,cola[r][k].last_marker_bit);
if((cola[r][k].last_ts==ultemp)&&!(cola[r][k].last_marker_bit))
{
actual_index=k;
cola[r][k].sts.long_media_frag_inst++;
if(cola[r][k].new_frame==TRUE)
{
cola[r][k].sts.num_medio_paq_fragmentados_inst++;
cola[r][k].sts.long_media_frag_inst++;
}
cola[r][k].new_frame=FALSE;
}
}
}
break;
case RTCP:
actual_index=R;
break;
default:
actual_index=UNK;
break;
}
}//Final del IF del UDP
break;
case TCP:
actual_index=UNK;
break;
default:
actual_index=UNK;
break;
}
//printf("Actual index %d\n",actual_index);
cola[r][actual_index].sts.num_medio_paquetes_inst++;
cola[r][actual_index].sts.long_media_paq_inst+=l-TUN_HEADER_SIZE;
num_paquetes_totales++;
II. Anexo II (Procedimiento para la comparación
de la calidad de dos vídeos)
Para poder comparar dos en streaming se realizaba el siguiente
Procedimiento:
- Se inicia la petición desde el cliente de streaming.
- En el cliente el video reproduce el contenido por pantalla. En este
momento se captura el vídeo mostrado mediante un programa
dedicado. En nuestro caso el programa utilizado era el.
- Una vez obtenemos el video en formato YUV guardado en un
fichero avi, se procede a realizar una alineación temporal de los
frames, para evitar errores provocados por la comparación de frames
desalineados.
- El video capturado que contiene el mismo número de frames
del original se procede a comparar el video con el software VQM.
Para realizar dicho procedimiento se han seguido los pasos
explicados en el manual del programa.
Fig. II.1 Esquema del montaje realizado para el streaming de video.