570 likes | 800 Views
Multimedia en Internet (Primera parte: Conceptos, Audio y video) Segunda Parte: Datos y Práctica. Dr. Agustín J. González Departamento de Electrónica http://www.elo.utfsm.cl/~agv Senacitel 2004. Contenido (segunda parte). Compartición de contenidos
E N D
Multimedia en Internet(Primera parte: Conceptos, Audio y video)Segunda Parte: Datos y Práctica Dr. Agustín J. González Departamento de Electrónica http://www.elo.utfsm.cl/~agv Senacitel 2004
Contenido (segunda parte) • Compartición de contenidos • Odust: una herramienta para compartir aplicaciones • Transmisión dinámica de Imágenes • Conclusiones y trabajo futuro • Programación TCP/IP en C • Programación TCP/IP en Java • Programación de audio en Linux • Programación de vídeo en Linux Senacitel 2004 Multimedia en Internet
Odust: un sistema para compartir aplicaciones • Modos de Colaboración: • Asincrónica: Ej. email, WEB • Sincrónica: Ej. Vídeo Conferencia • Componentes básicas de las aplicaciones multimediales sincrónicas: • Audio • Vídeo • Datos: • Pizarras compartidas • Aplicación cualquiera compartida • Problema: Además de audio y vídeo, las sesiones multimediales necesitan una componente para el envío de la idea principal en discusión. Senacitel 2004 Multimedia en Internet
Protocolo para la Transmisión de Imágenes Sintéticas • Soluciones tradicionales: • Uso de vídeo (limitaciones de tamaño, alto ancho de banda) • Aplicaciones compartidas: XTV, co-browsers, VNC, integrada en NetMeeting, http://www.marratech.com.. ( no escalan bien por uso de TCP). • “Nuestra” solución: • Protocolo similar al de vídeo, pensado para el envío de imágenes dinámicas Senacitel 2004 Multimedia en Internet
Posible Escenario de Uso Usuario: esparta Windows 98 Usuario: troya Windows XP Red Multicast Usuario: xx Linux Debian Usuario: yy Linux Fedora Senacitel 2004 Multimedia en Internet
Posible Escenario de Uso Usuario:esparta Windows 98 Usuario: troya Windows XP Red Multicast Senacitel 2004 Multimedia en Internet Usuario: xx Linux Debian Usuario: yy Linux Fedora
Posible Escenario de Uso Usuario: esparta Windows 98 Usuario: troya Windows XP Red Multicast Senacitel 2004 Multimedia en Internet Usuario: xx Linux Debian Usuario: yy Linux Fedora
Posible Escenario de Uso Usuario: troya Windows XP Usuario: esparta Windows 98 Red Multicast Usuario: yy Linux Fedora Usuario: xx Linux Debian Senacitel 2004 Multimedia en Internet
Como funciona? • Se logra compartir las aplicaciones a través de la distribución de imágenes de la aplicación corriendo en la pantalla de uno de los participantes. • Provee un mecanismo de control de turnos para permitir a cualquier participante operar la aplicación siendo compartida. • Escalabilidad es lograda a través del uso de UDP multicasting. • Para pocos participantes usa TCP Senacitel 2004 Multimedia en Internet
Odust: Transmisión de imágenes • Captura periódicamente la ventana • La imagen es dividida • Se elimina redundancia espacial y temporal y se envía Senacitel 2004 Multimedia en Internet
Receptor: Recibir unidad de datos (rectángulo) Descomprimir el rectángulo Actualizar la región de la imagen Transmisor: Eliminación de redundancia temporal Muestreo regular de la imagen Dividir imagen con cuadriculado Procesar sólo áreas con cambios Eliminación de redundancia espacial Comprimir y enviar áreas cambiadas Transmisión Dinámica de Imágenes Sintéticas Senacitel 2004 Multimedia en Internet
Sobreponiendose a las pérdidas • Cada rectángulo es retransmitido luego de un tiempo aleatorio (UDP). • Esto también acomoda a los atrasados al encuentro. • Estudio de desempeño • ¿Cómo seleccionar la técnica de compresión de cuadrados? (JPEG, GIF, PNG?) • ¿Hay un tamaño ideal de cuadrado? ¿De qué depende? • ¿Qué tan a menudo muestrear la imagen? • ¿Cómo podemos comparar dos cuadrados eficientemente? • ¿Cuál es tasa máxima de transmisión? ¿De qué depende? Senacitel 2004 Multimedia en Internet
Arquitectura General Senacitel 2004 Multimedia en Internet
Mismo sitio en receptor Última Mejora(2004), hacer visible el cursor Senacitel 2004 Multimedia en Internet
Conclusiones y Trabajo Futuro en Odust • Además de audio y vídeo la compartición de datos es una componente crucial en sistemas de colaboración multimedial. • La herramienta distribuye imágenes de la aplicación enviado cuadrados de actualización cuando se detectan cambios. Se usa retransmisiones para recuperarse de pérdidas debido a multicast. • Está basado en Java, excepto un pequeño número de métodos para la captura de imágenes. • Se está trabajando en el uso de H.263+ o MPEG como esquema de compresión para las imágenes de la aplicación. Senacitel 2004 Multimedia en Internet
Contexto y multiplexación en TCP/IP Procesos de usuario Procesos de usuario Procesos de usuario Procesos de usuario Aplicación Multiplexación por: puerto Protocolo transporte Protocolo red TCP UDP Transporte ICMP IP IGMP Red ARP Interfaz de Hardware RARP Enlace Senacitel 2004 Multimedia en Internet
Comunicación entre nodos vía TCP/IP manejan detalles de aplicación Borwser ServidorWEB Protocolo HTTP Procesos de usuarios Aplicación Protocolo TCP TCP TCP Transporte Kernel manejan detalles de comunicación Protocolo IP IP IP Red Protocolo Ethernet Driver Ethernet Driver Ethernet Enlace Senacitel 2004 Multimedia en Internet
Generalidades • La comunicación entre máquinas se logra a través de un conjunto de rutinas. La más conocida en C es la API de Socket (Socket Application Programming Interface). • A través de la interfaz de socket, las aplicaciones especifican los detalles de la comunicación como: protocolo se usar, si es una aplicación cliente o servidor, y máquina remota. • En el sistema operativo UNIX esta API es parte del SO. Otros sistemas operativos han creado una biblioteca (library) que implementa todos los llamados de la API de socket y así el código UNIX puede ser fácilmente compilado y ejecutado en Windows por ejemplo. Senacitel 2004 Multimedia en Internet
Modelo de comunicación de Socket y I/O en UNIX • El modelo empleado en socket para la comunicación entre aplicaciones remotas es similar al usado en la comunicación con cualquier dispositivo de I/O en UNIX. Enviar datos a una aplicación remota usa el mismo tipo de llamados que enviar datos a un archivo. • En UNIX cuando se abre un archivo se retorna un descriptor de archivo. Cuando se abre un socket también se retorna un descriptor. • Un descriptor es un entero. El SO usa este entero para entrar en una tabla que contiene todos los atributos del dispositivo al cual se refiere el descriptor. Senacitel 2004 Multimedia en Internet
Parámetros usados en la API de Sockets • Las mayores diferencias entre la comunicación vía sockets y manejo de archivos es la la forma de abrir el canal. • Para crear un sockets la aplicación debe especificar : • Protocolo de transporte (su familia y protocolo dentro de la familia) • Dirección de la máquina remota o dirección local donde se esperará por requerimientos remotos, • Es una cliente o un servidor, • El puerto asociado a la aplicación Senacitel 2004 Multimedia en Internet
Cliente/Servidor TCP: Secuencia de Pasos en C Cliente Servidor Crear un socket Crear un socket Vincularlo a un puerto Definir # conexiones en tránsito Conectarlo al servidor Aceptar un cliente Enviar/Recibir datos Enviar/Recibir datos Cerrar el socket Cerrar el socket Senacitel 2004 Multimedia en Internet
Scket de recepción de conexiones y atención de cientes en UDP ProcesoServidor ProcesoCliente Socket RecepciónyAtención Socket Cliente datos Senacitel 2004 Multimedia en Internet
int main(int argc, char * argv[]){char buf[256];int s, n, len;struct sockaddr_in name;/* Crea un socket: punto de contacto entre aplicación y capa transporte.*/ s = socket(AF_INET, SOCK_DGRAM, 0);/* Prepara estructura de datos para asociar el socket a un puerto. */ name.sin_family = AF_INET; name.sin_port = htons(atoi(argv[1])); name.sin_addr.s_addr = htonl(INADDR_ANY); /* en cualquier IP local.*//* Liga el socket al puerto deseado. */ len = sizeof(struct sockaddr_in); bind(s, (struct sockaddr *) &name, len);/* Lee desde el socket y escribe en pantalla hasta * llegada de datagrama con datos de largo cero. */while ((n = recv(s, buf, sizeof(buf), 0)) > 0) write(STDOUT_FILENO, buf, n); close(s); exit(0);} Cliente UDP Red Red Servidor UDP Servidor UDP en C simple Senacitel 2004 Multimedia en Internet
int main(int argc, char * argv[]){int n, s, len;char buf[256];struct hostent *hp;struct sockaddr_in name;/* busca consulta la direccion IP del la maquina a partir de su nombre. */ hp = gethostbyname(argv[1]);/* Crea un socket: punto de contacto entre aplicacion y capa transporte. */ s = socket(AF_INET, SOCK_DGRAM, 0);/* LLena la estructura que informa a capa UDP sobre direccion del destinatario */ name.sin_family = AF_INET; name.sin_port = htons(atoi(argv[2])); memcpy(&name.sin_addr, hp->h_addr_list[0], hp->h_length);/* Lee datos desde consola y los envia al servidor. Termina con Control-D (Fin de archivo) */ len = sizeof(struct sockaddr_in);while ((n = read(STDIN_FILENO, buf, sizeof(buf))) > 0) sendto(s, buf, n, 0, (struct sockaddr*) &name, len); /* envia datagrama con cero datos para indicar termino */ sendto(s, buf, 0, 0, (struct sockaddr*) &name, len); close(s); exit(0);} Cliente UDP Red Red Servidor UDP Cliente UDP en C simple Senacitel 2004 Multimedia en Internet
Scket de recepción de conexiones y atención de cientes en TCP ProcesoServidor ProcesoCliente Establecimientoconexión Socket Recepción Socket Cliente Socket Atención datos Senacitel 2004 Multimedia en Internet
Cliente TCP Red Red Servidor TCP Servidor TCP en C Simple int main(int argvc, char * argv[]){char buf[20];int s, n, ns, len;struct sockaddr_in name;/* Crea un socket: punto de contacto entre aplicacion y capa transporte.*/ s = socket(AF_INET, SOCK_STREAM, 0);/* Prepara estructura de datos para asociar el socket a un puerto. */ name.sin_family = AF_INET; name.sin_port = htons(atoi(argv[1])); name.sin_addr.s_addr = htonl(INADDR_ANY); /* en cualquier IP local.*//* Liga o asocia el socket al puerto deseado. */ len = sizeof(struct sockaddr_in); bind(s, (struct sockaddr *) &name, len);/* define cuantas conexiones pueden estar en proceso de aceptacion. */ listen(s, 4);/* Espera y acepta una conexion. Retorna un nuevo socket para atender a esa conexion */ ns = accept(s, (struct sockaddr *) &name, &len);/* Lee desde el socket y escribe en pantalla hasta * llegada de segmento con datos de largo cero. */while ((n = recv(ns, buf, sizeof(buf), 0)) > 0) write(STDOUT_FILENO, buf, n); close(ns); close(s); exit(0);} Senacitel 2004 Multimedia en Internet
Cliente TCP Red Red Servidor TCP Cliente TCP en C Simple int main(int argc, char * argv[]){int n, s, len;char buf[1024];struct hostent *hp;struct sockaddr_in name;/* busca consulta la direccion IP del la maquina a partir de su nombre. */ hp = gethostbyname(argv[1]);/* Crea un socket: punto de contacto entre aplicacion y capa transporte. */ s = socket(AF_INET, SOCK_STREAM, 0);/* LLena la estructura que informa a capa TCP sobre servidor a contactar */ name.sin_family = AF_INET; name.sin_port = htons(atoi(argv[2])); memcpy(&name.sin_addr, hp->h_addr_list[0], hp->h_length);/* EL cliente llama y se conecta con el servidor. */ len = sizeof(struct sockaddr_in); connect(s, (struct sockaddr *) &name, len);/* Ahora que el socket esta conectado, lee datos desde consola y los envia al servidor. Termina con Control-D (Fin de archivo) */while ((n = read(STDIN_FILENO, buf, sizeof(buf))) > 0) send(s, buf, n, 0); close(s); exit(0);} Senacitel 2004 Multimedia en Internet
Creación y cierre de un socket • Creación:int descriptor;descriptor = socket(protocolFamily, type, protocol);donde: • Familia de protocolo: PF_INET, PF_APPLETALK • Tipo: SOCK_STREAM para servicios de conexión y SOCK_DGRAM para servicios sin conexión. • Protocolo: El protocolo particular a ser usado de la familia de protocolo. En TCP/IP, éste puede ser tcp o udp. • Cierre del un socket:close(descriptor); Senacitel 2004 Multimedia en Internet
Procedimientos en servidor • Procedimiento bindEste procedimiento asocia o liga un socket con una dirección IP y un puerto local.bind(descriptor, local_addr, addr_len); • Descriptor: es el descriptor del socket. • Dirección local: es una estructura conteniendo IP local y puerto local. • Largo de dirección: entero que especifica el largo de la estructura local_addr. • El llamado se pensó genérico para acomodar varios protocolos. Senacitel 2004 Multimedia en Internet
Procedimientos en servidor (cont..) • struct sockaddr { u_char sa_len; // largo total u_char sa_family; // familia de la dirección char sa_data[14]; //la dirección } • En el caso particular de TCP/IP el campo dirección está especificado como sigue: • struct sockaddr_in { u_char sin_len; // largo total u_char sin_family; // familia de la dirección u_short sin_port; // número de puerto struct in_addr sin_addr; // dirección IP char sin_zero[8]; // no usados } Senacitel 2004 Multimedia en Internet
Procedimientos en servidor (cont..) • Procedimiento listenlisten( descriptor, queue_size);Instruye al OS que el socket es pasivo y desea tener a lo más queue_size requerimientos de conexión pendientes. • El sistema rechaza requerimientos cuando la cola se llena. • Procedimiento acceptnewDescriptor = accept(descriptor, c_address, c_address_len); • Esta llamada es usada por servidores que usan servicios de conexión. • c_address es del tipo struct sockaddr ya visto. • Luego de aceptar una conexión, el servidor atiende al cliente a través del descriptor especialmente creado para él. Mientras tanto el servidor puede aceptar nuevas conexiones en el descriptor original. Senacitel 2004 Multimedia en Internet
Procedimientos en cliente • Procedimiento connectconnect(descriptor, srv_address, srv_address_len); • srv_address: es una estructura del tipo struct sockaddr que contiene la dirección del servidor. • srv_address_len es el largo de la estructura. • Cuando el servicio es de conexión, connect establece la conexión con el servidor, el cual debe aceptar con accept(). Senacitel 2004 Multimedia en Internet
Procedimientos de envío y recibo • Procedimiento sendint send (descriptor, data, length, flags); • data es la dirección de memoria donde se encuentran los datos a enviar. • Length: es el largo en bytes de la zona de datos. Ésta función retorna el número de bytes efectivamente enviados • Flags: especifican opciones. Las aplicaciones normales no las usan. Son usadas por programas de monitoreo y debug. • Procedimiento sendtoint sendto (descriptor, data, length, flags, dest_address, address_len); Senacitel 2004 Multimedia en Internet
Procedimientos de envío y recibo • Procedimiento recvint recv (descriptor, buffer, length, flags); • buffer es la dirección de memoria donde se deben depositar los datos a recibir. • length: es el largo en bytes de los datos que se desean leer. Recv retorna el número de bytes efectivamente leídos. • Flags: especifican opciones. Las aplicaciones normales no las usan. Son usadas por programas de monitoreo y debug. • Procedimiento recvfromint recvfrom (descriptor, buffer, length, flags, source_address, address_len); Senacitel 2004 Multimedia en Internet
Otras funciones relacionadas • Procedimientos getpeername: permite obtener la dirección y puerta remota a partir de un socket ya “conectado”. • Procedimiento gethostname: permite obtener el nombre de la máquina local. • Procedimientos getsockopt y setsockopt: permiten obtener y modificar atributos de un socket; por ejemplo, el tramaño del buffer usado por TCP para recibir paquetes. • Procedimientos gethostbyname y gethostbyaddr: permiten obtener la información sobre una máquina a partir de su nombre o dirección IP. Senacitel 2004 Multimedia en Internet
Cliente/Servidor TCP: Secuencia de Pasos en Java Cliente Servidor Crear un socket Crear un socket Conectarlo al servidor Esperar y Aceptar un cliente Enviar/Recibir datos Enviar/Recibir datos Cerrar el socket Cerrar el socket Senacitel 2004 Multimedia en Internet
Cliente TCP Red Red Servidor TCP Servidor TCP en Java, Simple import java.io.*;import java.net.*;class TCPserver {publicstaticvoid main (String argv[]) throws Exceptio { String line; // Almacena lo recibido//welcomeSocket es el socker servidor que acepta la conexión ServerSocket welcomeSocket = new ServerSocket( Integer.parseInt(argv[0]));// connectionSocket es aquel que atiende a un cliente específico Socket connectionSocket = welcomeSocket.accept();// Esta concatenación de objetos adaptadores permite la lectura// simple de datos desde el socket para ese cliente. BufferedReader inFromClient = new BufferedReader(new InputStreamReader(connectionSocket.getInputStream()));// Recibe datos y los envia a pantalla.do { line=inFromClient.readLine(); System.out.println(line); } while(!line.equals("quit"));// Cerramos ambos sockets connectionSocket.close(); welcomeSocket.close(); }} Senacitel 2004 Multimedia en Internet
Cliente TCP Red Red Servidor TCP Cliente TCP en Java , Simple import java.io.*;import java.net.*;class TCPclient {publicstaticvoid main (String argv[]) throws Exception { String line; // Almacena lo digitado// Concatenación de objetos adaptadores para la lectura// simple de teclado. BufferedReader inFromUser = new BufferedReader( new InputStreamReader(System.in));// Socket en el cliente para enviar datos al servidor. Socket clientSocket = new Socket(argv[0],Integer.parseInt(argv[1]));// Concatenación de objetos adaptadores para la escritura// o envio de datos en forma simple a través del socket. DataOutputStream outToServer = new DataOutputStream( clientSocket.getOutputStream());// Lectura de teclado y envío de datos al servidor.do { line=inFromUser.readLine(); outToServer.writeBytes(line+'\n'); } while(!line.equals("quit"));// Cerramos el socket y con ello también la conexión. clientSocket.close(); }} Senacitel 2004 Multimedia en Internet
Cliente TCP Red Red Servidor TCP Servidor UDP en Java, Simple import java.io.*;import java.net.*;class UDPserver {publicstaticvoid main (String argv[]) throws Exception {// construimos un socket ligado a un puerto. Pasa a ser servidor. DatagramSocket serverSocket = new DatagramSocket( Integer.parseInt(argv[0]));// buffer que contendrá los datos recibidosbyte[] receiveData = newbyte[256];// Datagrama que recibe lo enviado por el cliente. DatagramPacket receivePacket = new DatagramPacket (receiveData, receiveData.length); String line; // almacenará la linea enviada.do { serverSocket.receive(receivePacket); // Recibimos un datagrama// y extraemos de él la línea enviada desde la posición 0 // al largo de datos recibidos. line = new String(receivePacket.getData(), 0, receivePacket.getLength()); System.out.print(line); // muestra línea en pantalla. }while(!line.equals("quit"+'\n'));// Cerramos ambos sockets serverSocket.close(); }} Senacitel 2004 Multimedia en Internet
Cliente TCP Red Red Servidor TCP Cliente UDP en Java, Simple import java.io.*;import java.net.*;class UDPclient {publicstaticvoid main (String argv[]) throws Exception {// Concatenación de objetos adaptadores para la lectura// simple de teclado. BufferedReader inFromUser=new BufferedReader(new InputStreamReader( System.in));// Socket en el cliente para enviar datos al servidor. DatagramSocket clientSocket = new DatagramSocket();// Creamos objeto con dirección IP destino InetAddress IPAddress = InetAddress.getByName(argv[0]);// Puerto a definir en el datagrama a enviarint port = Integer.parseInt(argv[1]); String line; // linea a leer de tecladodo { line = inFromUser.readLine()+'\n'; byte[] sendData = line.getBytes(); // sacamos los bytes del string// se construye el Datagrama UDP con los datos, dirección y puerto destino DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length,IPAddress,port);// enviamos el datagrama clientSocket.send(sendPacket); }while (!line.equals("quit"+'\n'));// Cerramos el socket clientSocket.close(); }} Senacitel 2004 Multimedia en Internet
Drivers y bibliotecas de sonido • OSS (Open sound system) driver: soportado en varias plataformas UNIX y tarjetas. Hoy es un producto comercial ($$). Se puede bajar para evaluación desde: http://www.opensound.com • ALSA (Advanced Linux Sound Architecture) es un driver que puede ser usado en lugar del driver del kernel. Hoy ya es parte del kernel de linux (>= versión 2.5). • Ambos proveen un API para programar aplicaciones. Senacitel 2004 Multimedia en Internet
Aplicación A/D Hardware + driver Aplicación D/A Hardware + driver Cosas a tener en cuenta • La captura de muestras es constante, la aplicación las lee muestras desde un buffer circular. • La reproducción es también constante, la aplicación escribe muestras en un buffer de salida • Supondremos que la máquina ya tiene posporte para audio Senacitel 2004 Multimedia en Internet
Audio básico • Si su máquina tiene soporte de sonido, usted encontrará los dispositivos:/dev/audio/deb/dsp • Verifique la configuración para la entrada y salida de audio en su máquina. Ver alsamixer.Para definir como entrada: espacioPara mute : mLo demás con las flejas de cursor. • La salida es controlada por le volumen maestro más el de al menos una fuente. • La captura es controlada con el volumen de captura más la selección de la fuente (mic por ejemplo). • Prueba básica: %cat /dev/audio > myAudio%cat myAudio > /dev/audioIdem con /dev/dsp Senacitel 2004 Multimedia en Internet
Estructura general de un programa Abrir_el_dispositivo(); Definir_los_parámetros_del_dispositivo(); while (!done) { /* Uno, el otro o ambos de éstos */ recibir_datos_de audio_desde_el_dispositivo(); enviar_datos_de audio_al dispositivo(); } Cerrar_el_dispositivo() Senacitel 2004 Multimedia en Internet
Mic.c: Programa capturador básico en linux int main(int argc, char * argv[]) {int fd, value, nSamples;char buff[1024];if ((fd = open("/dev/dsp", O_RDONLY)) < 0) {if ((errno == EINTR) || (errno == EBUSY)) { printf("Audio device is Busy... \n"); exit(-1); } printf("Error opening audio device ... \n"); exit(-1); }/* Define Mono*/ value=0; ioctl(fd,SNDCTL_DSP_STEREO,&value);/* Set Audio format to the 8 bits per sample.*/ value=AFMT_S8; ioctl(fd,SNDCTL_DSP_SETFMT, &value);/* Set sample rate*/if (argc ==2 ) value = atoi(argv[1]);else value = 8000; ioctl(fd,SNDCTL_DSP_SPEED, &value);while(1) { nSamples=read(fd, buff, sizeof(buff)); write(STDOUT_FILENO, buff, nSamples); }} Senacitel 2004 Multimedia en Internet
Speaker.c: Programa reproductor básico en linux int main(int argc, char * argv[]) {int fd, value, nSamples;char buff[1024]; printf("usage: %s [sample rate in Hz]\n", argv[0]);if ((fd = open("/dev/dsp", O_WRONLY)) < 0) {if ((errno == EINTR) || (errno == EBUSY)) { printf("Audio device is Busy... \n"); exit(-1); } printf("Error opening audio device ... \n"); exit(-1); }/* Define modo */ value=0; ioctl(fd,SNDCTL_DSP_STEREO,&value);/* configura el formato de audioa 8 bits por muestra */ value=AFMT_S8; ioctl(fd,SNDCTL_DSP_SETFMT, &value); /* Configura la tasa de muestreo */if (argc==2) value= atoi(argv[1]);else value=8000; ioctl(fd,SNDCTL_DSP_SPEED, &value);while(1) { nSamples=read(STDIN_FILENO, buff, sizeof(buff)); write(fd, buff, nSamples); }} Senacitel 2004 Multimedia en Internet
Programación más completa de audio • Estudiar la API de alsa. Revisar las páginas del Proyecto ALSA • En particular: A Tutorial on Using the ALSA Audio API • Ejemplo utilitarios: arecord y aplay Senacitel 2004 Multimedia en Internet